Skip Headers
Oracle® Application Server Advanced Web Services Developer's Guide
10g Release 3 (10.1.3)
B25603-01
  Go To Documentation Library
Home
Go To Product List
Solution Area
Go To Table Of Contents
Contents
Go To Index
Index

Previous
Previous
Next
Next
 

7 Custom Serialization of Java Value Types

This chapter describes the Custom Serialization Framework API. This API enables you to map and serialize Java value types between schema-defined XML types and Java value type classes that do not conform to the JAX-RPC value type requirements and cannot be handled by the default JAX-RPC serialization mechanism. This framework can also be used to implement custom mapping for the schema types that have default mappings. For example, you can use the framework to map xsd:dateTime to the custom class com.hello.MyDate instead of its default mapping java.util.Calendar.

The Custom Serialization Framework API can be used to serialize Java value types for publishing a Web service top down, bottom up, or for generating Java value types from a schema to publish a Web service endpoint.

The built-in data type mappings that OracleAS Web Services supports can be found in Appendix D, "Mapping Java Types to XML and WSDL Types" and in the JAX-RPC 1.1 specification.

http://java.sun.com/webservices/jaxrpc/index.jsp

You do not need to create custom mappings for these Java value types."OC4J Support for Java Value Types" and Chapter 5.4 of the JAX-RPC 1.1 specification lists the requirements for Java value types.

The Custom Serialization Framework API

If you want to expose a custom or non-standard Java value type in your Web service, OracleAS Web Services provides an API that lets you serialize them into an XML representation. The SOAPElementSerializer interface in the oracle.webservices.databinding package defines the interface for pluggable custom serializers for the OracleAS Web Services JAX-RPC implementation.

public interface SOAPElementSerializer

The serializer converts a Java object to an XML representation of SAAJ SOAPElement. An instance of SOAPElementSerializer serializes a Java object to a SOAPElement and deserializes a SOAPElement to a Java object.

The interface has the following methods.

An implementation of the interface requires a default no-argument constructor. For more information on the SOAPElementSerializer interface, see the output of the Javadoc tool.

Using Custom Serialization in Web Service Development

To use the custom serialization in your Web service, you provide an implementation of the SOAPElementSerializer for your custom Java type value class. You must also provide the custom Java value type class and an XML configuration file that defines the mapping between the Java type value class and the XML schema type it should represent. You can find more information about these files in "Developing a Custom Serializer Implementation for a Java Type Value Class", "Defining a Custom Java Type Value Class", and "Creating an oracle-webservices Type Mapping Configuration".

The following sections describe how you can use custom serialization in top down, bottom up and schema-driven Web service development.

Implementing a Custom Serializer

This section describes the steps in developing a custom serializer for a Java value type class. Typically, you start with the custom Java value type class that you want to express as an XML schema type. From there, you develop a custom serializer implementation of the class, the service endpoint interface, and its implementation. The oracle-webservices type mapping configuration XML file identifies the schema type that will be used to represent the custom Java type value class.

For top down Web service assembly, the service endpoint interface is generated from a WSDL. For bottom up Web service assembly, develop a Java service endpoint interface

Defining a Custom Java Type Value Class

This section illustrates a custom Java type value class that can be used to map to an XML schema type.

Note that the custom Java type class does not have to be Serializable if the Web service is not going to be used with any J2EE features that require Serializable (such as EJB or JMS).

An empty constructor is not a requirement. The serializer implementation that you write programmatically constructs the instance of the corresponding Java type and populates the content of the Java object. This is why you want to use the custom serializer. It provides full control over the XML-to- Java and Java-to-XML mappings.

In JAX-RPC, xsd:dateTime can be represented by either java.util.Calendar or java.util.Date. If you do not want to use either of these classes to represent a date, you can implement your own date class. Example 7-1 illustrates a custom Java value class, oracle.demo.custom_type.MyDate, that can be used to represent a date.

Example 7-1 Sample Custom Java Type Value Class

package oracle.demo.custom_type;

public class MyDate implements java.io.Serializable{
    
    private String string;
    
    public MyDate(String s) {
        string = s;
    }
 
    public String getString() {
        return string;
    }
    
    public void setString(String string) {
        this.string = string;
    }
    
}

Developing a Custom Serializer Implementation for a Java Type Value Class

To use a Java type value class in a Web service implementation and to allow an XML schema type to represent the class in a WSDL, you must provide an implementation of SOAPElementSerializer for the Java type value class. The implementation will handle the serialization and deserialization between the Java type value class and the XML schema type.

Example 7-2 illustrates an implementation of SOAPElementSerializer for the MyDate value type class described in "Defining a Custom Java Type Value Class". The implementation will allow MyDate to be used in the Web service endpoint interface and to handle serialization and deserialization between MyDate and xsd:dateTime. It will also allow the xsd:dateTime schema type to represent the MyDate class in the generated WSDL.

Example 7-2 Sample SOAPElementSerializer Implementation

package oracle.demo.custom_type;
import oracle.webservices.databinding.SOAPElementSerializer;
import javax.xml.soap.SOAPFactory;
import javax.xml.soap.SOAPException;
import javax.xml.soap.SOAPElement;
import javax.xml.soap.Text;
import javax.xml.namespace.QName;
import java.util.Iterator;
import java.util.Map;
public class MyDateSerializer implements SOAPElementSerializer {
    private Map initParams;
    protected SOAPFactory soapFactory;
    public MyDateSerializer() {
    }
    public void init(Map prop) {
        initParams = prop;
        try {
            soapFactory = SOAPFactory.newInstance();
        } catch (SOAPException e) {
            e.printStackTrace();
        }
    }
    public Object deserialize(SOAPElement ele) {
        String str = "";
        for( Iterator i = ele.getChildElements(); i.hasNext();) {
            Object obj = i.next();
            if( obj instanceof Text ) {
                str += ( ((Text)obj).getValue() );
            }
        }
    return new MyDate(str);
    }
    /**
    * The qname parameter must be used to create the top level element.
    */
    public SOAPElement serialize(QName qname, Object obj) {
        SOAPElement xml = null;
        try {
            xml = soapFactory.createElement(
            qname.getLocalPart(),
            qname.getPrefix(),
            qname.getNamespaceURI());
            xml.addTextNode( ((MyDate)obj).getString() );
        } catch (SOAPException e) {
            e.printStackTrace();
        }
        return xml;
    }
}

Developing a Service Endpoint Interface that Uses a Java Type Value Class

Develop a Java service endpoint interface that will be exposed as the Web service. Example 7-3 and Example 7-4 illustrate a Java service endpoint interface and implementation that use the oracle.demo.custom_type.MyDate class.

Note that to satisfy JAX-RPC requirements, the interface extends java.rmi.Remote and the methods throw java.rmi.RemoteException.

The MyDateServiceEndpoint example also employs a user-defined bean type, MyDateBean, as a parameter. MyDateBean has two properties of MyDate: beginDate and endDate. WebServicesAssembler will generate the schema for MyDateBean because MyDate is configured to use the custom serializer and MyDateBean conforms to the JAX-RPC Value Type (Bean) patterns. Example 7-5 illustrates MyDateBean.

Example 7-3 Sample Java Service Endpoint Interface

package oracle.demo.custom_type;
 
import oracle.demo.custom_type.MyDate;
 
public interface MyDateServiceEndpoint extends java.rmi.Remote {
 
    public MyDate echoMyDate(MyDate date)
            throws java.rmi.RemoteException;
 
 
    public MyDateBean echoMyDateBean(MyDateBean bean)
            throws java.rmi.RemoteException;
 
}

Similarly, note that new methods defined in the implementation illustrated in Example 7-4 throw a java.rmi.RemoteException.

Example 7-4 Sample Implementation of a Java Service Endpoint Interface

package oracle.demo.custom_type;
 
public class MyDateServiceEndpointImpl implements MyDateServiceEndpoint {
 
    public MyDate echoMyDate(MyDate date) throws java.rmi.RemoteException {
        return date;
    }
 
    public MyDateBean echoMyDateBean(MyDateBean bean) throws 
               java.rmi.RemoteException {
        return bean;
    }
 
}

Example 7-5 illustrates a user-defined bean, MyDateBean.

Example 7-5 Sample User-Defined Bean Type

package oracle.demo.custom_type;
 
public class MyDateBean implements java.io.Serializable{
    
    private MyDate beginDate;
    private MyDate endDate;
    
    public MyDate getBeginDate() {
        return beginDate;
    }
 
    public void setBeginDate(MyDate beginDate) {
        this.beginDate = beginDate;
    }
    
    public MyDate getEndDate() {
        return endDate;
    }
    
    public void setEndDate(MyDate endDate) {
        this.endDate = endDate;
    }
}

Creating an oracle-webservices Type Mapping Configuration

Create an oracle-webservices type mapping configuration XML file to specify the XML schema type that will be used to represent the custom Java type value class. The contents of the <type-mapping> element specify the XML schema type, the custom Java type value class, and the class that performs the custom serialization. Table 7-1 describes the contents of the <type-mapping> element.

Table 7-1 Contents of the <type-mapping> Element

Attribute or Element Name Description

java-class

Attribute that identifies the Java type value class

xml-type

Attribute that identifies the XML schema type

serializer-class

Element that identifies the class that performs the custom serialization


Use the ddFileName argument to specify the oracle-webservices type mapping configuration to WebServicesAssembler.

The schema files oracle-webservices-10_0.xsd and oracle-webservices-types-10_0.xsd can be found in the oc4j-schemas.jar file in the OC4J_HOME/j2ee/home/lib/ directory.

Example 7-6 illustrates an oracle-webservices type mapping configuration that tells WebServicesAssembler to map the oracle.demo.custom_type.MyDate class to xsd:dateTime. The <serializer-class...> element identifies the MyDateSerializer class developed in Example 7-2.

Example 7-6 Sample oracle-webservices Type Mapping Configuration

<oracle-webservices>
    <webservice-description name="MyDateService">
        <type-mappings xmlns:xsd="http://www.w3.org/2001/XMLSchema">
            <type-mapping
                java-class="oracle.demo.custom_type.MyDate"
                xml-type="xsd:dateTime">
                <serializer class="oracle.demo.custom_type.MyDateSerializer"/>
            </type-mapping>
        </type-mappings>
    </webservice-description>
</oracle-webservices>

Using Custom Types in Client-Side Proxy Code

By default, WebServicesAssembler does not configure custom types when generating the Web service client-side proxy. The generated proxy will use the default JAX-RPC Java value type-to-XML schema type mappings. For the MyDate example presented in this section, this means that the default Java value type, java.util.Calendar, will be used to represent xsd:dateTime.

To configure custom types into your generated client proxy code, use the ddFileName and classpath arguments to genProxy to specify the oracle-webservices type mapping configuration XML file. The classpath must include the value types and custom serializer. This will allow you to use the custom Java classes in your client-side proxy code. For the example in this section, providing the oracle-webservices type mapping configuration and its classpath when you generate the client-side proxy code enables you to use MyDate instances for xsd:dateTime in the Web service client-side proxy.

Example 7-7 assumes that the client-side proxy was generated with an oracle-webservices type mapping configuration XML file. Note the use of instances of MyDate instead of the default mapping, Calendar.

Example 7-7 Client-Side Proxy Code, Using Custom Java Type Values

ServiceFactory factory = ServiceFactory.newInstance();
        MyDateService service =  (MyDateService)factory.loadService(MyDateService.class);
        MyDateServiceEndpoint proxy = service.getMyDateServiceEndpointPort();
        ((Stub)proxy)._setProperty( Stub.ENDPOINT_ADDRESS_PROPERTY, address );
        MyDate req = new MyDate("Year:2004, Month:5, Date:24");
        MyDate res = proxy.echoMyDate( req );

        MyDate begin = new java.util.GregorianCalendar(1977, 5, 24);
        MyDate end = new java.util.GregorianCalendar(2004, 5, 24);
        ...

Using Custom Serialization in Top Down Web Service Development

You can use the custom serializer to provide special Java value type-to-XML schema type mappings for a custom Java value type that you want to use in the service endpoint interface. This means that you can substitute a custom Java value type for a type that has a defined, default JAX-RPC mapping in the WSDL. WebServicesAssembler will process the custom Java value type and generate the correct code for the interface.

For example, you can change the default mapping of the schema-defined XML type xsd:dateTime from java.util.Calendar, to a custom Java value type class, hello.MyDate. The custom serializer will allow you to generate the correct code for the service endpoint interface.

Other than providing the custom Java value type class, the SOAPElementSerializer implementation, and the type mapping configuration file, this is very similar to the generic top down Web service development.

Developing a Web service that uses a custom serializer follows these general steps.

  1. Run WebServicesAssembler with the genInterface command and the custom serializer to generate the service endpoint interface with the custom type mappings.

  2. Implement the service endpoint interface.

  3. Run WebServicesAssembler again with the topdownAssemble command to assemble the Web service and package it in an EAR.

The following sections describe these steps in greater detail.

Prerequisites

Before you begin, provide the following files and information.

How to Use Custom Serialization in Top Down Web Service Development

The following steps describe how to use custom serialization in top down Web service development.

  1. Provide the files described in the Prerequisites section as input to the WebServicesAssembler genInterface command. For example:

    java -jar wsa.jar -genInterface 
                     -ddFileName myOracleWSDescriptor.xml
                     -output build/src/service  
                     -wsdl wsdl/MyWSDL.wsdl 
                     -unwrapParameters false 
                     -packageName oracle.demo.customdoclit.service 
    
    

    The major difference between using a custom serializer and standard top down Web services development, is the presence of the ddFileName argument. WebServicesAssembler will use the mappings specified in myOracleWSDescriptor.xml to generate the service endpoint interface.

    The WebServicesAssembler tool generates a Java interface for every port type specified in the WSDL and a Java Bean for each complex type. The myOracleWSDescriptor.xml provides the custom mappings that are specified.

    The name of the directory that stores the generated interface is based on the values of the output and packageName arguments. For this example, the generated interface is stored in build/src/service/oracle/demo/customdoclit/service.

  2. Compile the generated interface and type classes.

    For example:

    javac -destdir ${service.classes.dir} 
          -excludes {javac.excludes} 
          -path build/src/service 
          -classpath ${wsdemo.common.class.path}
    
    

    If you have set your CLASSPATH to include the referenced libraries described in "Web Service Client APIs and JARs" in the Oracle Application Server Web Services Developer's Guide, you can omit the -classpath argument. Otherwise, ensure that all of these libraries are contained in the path represented by ${wsdemo.common.class.path}. If you are running an Ant example, the appropriate settings are made in the Ant build script.

  3. Implement the service endpoint interface.

    The implementation must have a method signature that matches every method in the generated Java interface.

  4. Compile the service implementation.

    For example, you can use the same command as in Step 2 if the interface source was generated in the same directory where the Impl class was saved. If it was not, you must change the value of the path argument.

  5. Provide the service endpoint implementation, the WSDL, the custom Java value type class(es), the SOAPElementSerializer implementation(s), and the type mapping configuration XML file as input to the WebServicesAssembler topdownAssemble command.

    java -jar wsa.jar - topDownAssemble
                      -ddFileName myOracleWSDescriptor.xml
                      -output ./build
                      -wsdl wsdl/MyWSDL.wsdl
                      -unwrapParameters false
                      -packageName oracle.demo.customdoclit.service
                      -className oracle.demo.customdoclit.service.MyEndpointImpl
                      -appName MyWebService
                      -ear ./build/MyWebService.ear
    
    

    WebServicesAssembler generates and packages a deployable EAR file.

  6. Use WebServicesAssembler to generate the Web services client-side proxy.

    • generate stubs (client proxies) for a J2SE Web service client by running the WebServicesAssembler tool with the genProxy command, or

    • generate a service endpoint interface and a JAX-RPC mapping file for a J2EE Web service client by running the WebServicesAssembler tool with the genInterface command.

    For more information on generating and assembling client-side code, see "Assembling a J2EE Web Service Client" and "Assembling a J2SE Web Service Client" in the Oracle Application Server Web Services Developer's Guide.

    To use MyDate to represent xsd:dateTime in your client-side proxy code, you must use ddFileName to specify the oracle-webservices type mapping configuration XML file and classpath to provide the path to the custom Java class MyDate.

    For example, the following command generates client proxies (stubs) that can be used for a J2SE client.

    java -jar wsa.jar -genProxy
        -wsdl http://localhost:8888/custom_type_mydate/custom_type_mydate?WSDL
        -packageName oracle.demo.custom_type.mydate_with_ser
        -output ./build/src/client 
        -ddFileName MyClientSideDD.xml
        -classpath ./build/classes/service
    
    

    For information on coding a client-side proxy to use custom types, see "Using Custom Types in Client-Side Proxy Code".

Using Custom Serialization in Bottom Up Web Service Development

You can use the custom serializer to provide special Java value type-to-XML schema type mappings of some specific Java value types used in the service endpoint interface. This means that you can use your own custom schema document which describes special Java value types-to-XML schema type mappings. When you provide special Java value type-to-XML schema type mappings, you do not want WebServicesAssembler to generate schema definitions for these types.

Because this is bottom up Web service development, WebServicesAssembler will generate a WSDL. In the WSDL, instead of generating the schema for the value types, WebServicesAssembler will import one or more schema documents you specify.

Prerequisites

Before you begin, provide the following files and information.

How to Use Custom Serialization in Bottom Up Web Service Development

The following instructions use sample code to develop a Web service bottom up, that requires custom type mapping.

  1. Provide the files described in the Prerequisites section as input to WebServicesAssembler assemble command. For example:

    java -jar wsa.jar -assemble 
        -targetNamespace http://oracle.com/ws_demo/custom_type_mydate 
        -typeNamespace http://oracle.com/ws_demo/custom_type_mydate 
        -input ./build/classes/service
        -classpath ./build/classes/service
        -output ./dist
        -ddFileName ./MyOracleWSDescriptor.xml
        -interfaceName oracle.demo.custom_type.MyDateServiceEndpoint
        -className oracle.demo.custom_type.MyDateServiceEndpointImpl
        -serviceName MyDateService
        -appName custom_type_mydate
    
    

    This command is similar to the standard bottom up Web services development except that the ddFileName argument is used to specify the oracle-webservices type mapping configuration XML file. The input argument contains the custom serializer implementation.

    WebServicesAssembler generates the WSDL and packages a deployable EAR file.

  2. Use WebServicesAssembler to generate the Web service client-side proxy. To use MyDate to represent xsd:dateTime in your client-side proxy code, you must use ddFileName to specify the oracle-webservices type mapping configuration file and classpath to provide the path to the custom Java class MyDate.

    java -jar wsa.jar -genProxy
        -wsdl http://localhost:8888/custom_type_mydate/custom_type_mydate?WSDL
        -packageName oracle.demo.custom_type.mydate_with_ser
        -output ./build/src/client 
        -ddFileName ./MyClientSideDD.xml
        -classpath ./build/classes/service
    
    

    For information on coding a client-side proxy to use custom types, see "Using Custom Types in Client-Side Proxy Code".

Ant Tasks for Generating a Web Service

This release provides Ant tasks for Web service development. The following examples show how the WebServicesAssembler commands in the preceding examples can be rewritten as Ant tasks.

For the assemble command, here is an example Ant task.

<oracle:assemble targetNamespace="http://oracle.com/ws_demo/custom_type_mydate"
    typeNamespace="http://oracle.com/ws_demo/custom_type_mydate"
    input="./build/classes/service"
    classpath="./build/classes/service"
    output="./dist"
    ddFileName="./custom_type_mydate_pdd.xml"
    serviceName="MyDateService"
    appName="custom_type_mydate"
    >
        <oracle:porttype
            interfaceName="oracle.demo.custom_type.MyDateServiceEndpoint"
            className="oracle.demo.custom_type.MyDateServiceEndpointImpl">
        </oracle:porttype>
</oracle:assemble>

For the genProxy command, here is an example Ant task.

<oracle:genProxy  
          wsdl="http://localhost:8888/custom_type_mydate/custom_type_mydate?WSDL"
          packageName="oracle.demo.custom_type.mydate_with_ser"
          output="./build/src/client" 
          ddFileName="./MyOracleClientDD.xml"
          classpath="./build/classes/client"
           />

Using Custom Serialization in Schema-Driven Web Service Development

In schema-driven Web service development, you invoke WebServicesAssembler with the schema document to generate the Java value type classes (beans) before you assemble the Web service.

Because the Java value classes generated from a schema complex type conform to the JAX-RPC value type (bean) pattern requirement, a custom serializer is not necessary. If you are satisfied with the default mapping provided by JAX-RPC, you do not need to implement the a custom serializer.

However, if you want to change the default marshaling between XML and Java, then you must implement the custom serializer for the generated Java value type classes.

Assembling a Web service from a schema follows these general steps.

  1. Run WebServicesAssembler with the schema document to generate the Java Value Type classes, the standard JAX-RPC mapping file, and the oracle-webservices type mapping configuration XML file (custom-type-mappings.xml).

  2. Develop your own service endpoint interface and its implementation.

  3. If you are not happy with the default marshalling for one or more classes, write a custom serializer to implement your own marshalling logic.

  4. Edit the oracle-webservices type mapping configuration XML file to include the custom serializer. You can also substitute an alternative Java class for any of the generated Java value type classes.

  5. Run WebServicesAssembler again, this time, to generate a WSDL document. Input the JAX-RPC mapping file, the edited oracle-webservices type mapping configuration XML file, the schema document, the generated Java value types, and the service endpoint interface and its implementation. Also include the custom serializer, if one was created.

    The generated WSDL will import the specified schema instead of generating one from the Java value types.

WebServicesAssembler will assemble a deployable EAR with a generated WSDL and all of the implementation files and deployment descriptors to enable a Web service.

Prerequisites

Before you begin, provide the following files and information.

  • the schema document. "Sample Schema Document" lists a schema document that defines types can be exposed through a service endpoint interface.

  • the definition of a custom Java type that will represent the complex type in the schema.

  • if you are providing custom default marshalling logic, you must also provide a custom serializer to implement your own marshalling logic.

Schema-Driven Web Services Assembly with Custom Serialization

This section describes how to develop a Web service from a XML schema that contains complex types. It also describes how to provide a custom serializer to handle serialization and deserialization between the complex type and a Java custom type.

"Sample Schema Document" describes the schema used in this example. The schema contains two complex types: InitItem and StringItem.

The section assumes that you want to use the custom Java type, oracle.webservices.examples.customtypes.MyInitItem, described in "Java Custom Type Implementation", to represent the InitItem complex type in the schema. To do this, you must provide a custom serializer to handle serialization and deserialization between the Java instance of MyInitItem and XML instance of InitItem.

For the complex schema type StringItem, the section assumes that you want to use the default JAX-RPC value type data binding mechanism to generate a value type class to represent the complex type. It also assumes that you want to do custom formatting on the XML instances of xsd:string. To do this, a custom serializer will be created to handle serialization and deserialization between Java instance of java.lang.String and the XML instance of xsd:string.

The following steps describe how to develop this Web service.

  1. Run WebServicesAssembler with the schema document to generate the Java value types and the standard JAX-RPC mapping file.

    java -jar wsa.jar -genValueTypes 
                      -packageName oracle.webservices.examples.customtypes
                      -schema ./MySchema.xsd 
                      -output ./build/src/types
    
    

    The schema document used in this command is described in "Sample Schema Document". The genValueTypes command generates two classes from this schema InitItem and StringItem. The InitItem class is not needed since you will be replacing it with your custom type class.

    This command also generates a JAX-RPC mapping file, jaxrpc-mappings.xml, which describes the binding of the generated value type classes, and a custom-type-mappings.xml file to record which value type classes are generated from an existing schema file. The custom-type-mappings.xml file is a generated server-side instance of the OracleAS Web Services type mapping configuration XML file.

  2. Compile the generated value types to the /build/classes/service directory.

    The genValueTypes command does not compile any classes. You must perform this step yourself. Note that you can either delete the IntItem class or exclude it from your compilation. This is because you will be providing a custom serializer for it.

  3. Implement the custom serializers. In this example, two custom serializers are required: a serializer to map between oracle.webservices.examples.customtypes.MyIntItem and InitItem and a serializer to map between java.lang.String and xsd:string.

    "Defining a Serializer Implementation with Marshalling Logic" contains a sample implementation of these serializers.

  4. Develop the Service Endpoint interface and its implementation.

    "Developing a Service Endpoint Interface and Implementation" provides a sample implementation of a service endpoint interface that uses oracle.webservices.examples.customtypes.MyInitItem and the generated Java value type classes.

  5. Edit the custom-type-mappings.xml file to include an alternative Java class and to include the custom serializer. In Step 1, the custom-type-mappings.xml file was generated into the output directory /build/src/types.

    Edit the file to use oracle.webservices.examples.customtypes.MyInitItem and its custom serializer, and to use the oracle.webservices.examples.customtypes.MyStringSerializer custom serializer. For details about adding the alternative Java class and custom serializers, see "Editing the Generated oracle-webservices Type Mapping Configuration XML File".

  6. Run the WebServicesAssembler tool again, this time with the assemble command, to generate the WSDL and a deployable EAR file. Use the following items as input.

    • the XML schema document (MySchema.xsd)

    • the value type classes generated in Step 1 (contained in the build/classes.service directory)

    • the JAX-RPC mapping file (jaxrpc-mappings.xml) generated in Step 1

    • the compiled custom serializer classes created in Step 2 (contained in the build/classes.service directory)

    • the service endpoint interface and its implementation (MyEndpointIntf and MyEndpointImpl) created in Step 3

    • the modified custom type mapping file MyCustomTypeMappings.xml created in Step 4

    Following is a sample assemble command.

    java -jar wsa.jar -assemble 
              -schema ./MySchema.xsd
              -mappingFileName ./build/src/types/jaxrpc-mappings.xml
              -input ./build/classes/service 
              -classpath ./build/classes/service 
              -output ./dist
              -ddFileName ./MyCustomTypeMappings.xml
              -interfaceName oracle.webservices.examples.customtypes.MyEndpointIntf
              -className oracle.webservices.examples.customtypes.MyEndpointImpl
              -appName MyCustomTypes
    
    

    The input directory, build/classes/service, contains the compiled value type classes generated in Step 1 and the compiled custom serializer files created in Step 2. The class name of each custom serializer is listed in the oracle-webservices type mapping file MyCustomTypeMappings.xml. Use the ddFileName argument to specify this file.

    The WebServicesAssembler tool assembles a deployable EAR file in the /dist directory. The generated WSDL in the EAR file imports the schema document.

    Note that this command is similar to normal Java-to-WSDL, or bottom up Web services assembly.

  7. Deploy the service and bind the application.

    Deploy EAR files in the standard manner into a running instance of OC4J. For more information on deploying EAR files, see the Oracle Containers for J2EE Deployment Guide. The following is a sample deployment command:

    java -jar <OC4J_HOME>/j2ee/home/admin_client.jar deployer:oc4j:localhost:port <user> <password>      
                 -deploy 
                 -file ./dist/MyCustomTypes.ear 
                 -deploymentName MyCustomTypes 
                 -bindWebApp default-web-site
    
    
  8. Develop the client.

    • generate stubs (client proxies) for a J2SE Web service client by running the WebServicesAssembler tool with the genProxy command, or

    • generate a service endpoint interface and a JAX-RPC mapping file for a J2EE Web service client by running the WebServicesAssembler tool with the genInterface command.

    For an example of developing a J2SE client-side proxy that uses oracle.webservices.examples.customtypes.MyInitItem to represent InitItem and the custom serializer to handle the marshalling and unmarshaling of data between the client and server, see "Developing a Client for Custom Type Mapping and a Custom Serializer".

Sample Schema Document

Example 7-8 illustrates the MySchema.xsd schema document that provides types that will be exposed as a service endpoint interface. The InitItem and StringItem are complex types.

Example 7-8 Sample Schema Document

<schema
   targetNamespace="http://examples.webservices.oracle/customtypes"
   xmlns:tns="http://ws.oracle.com/types"
   xmlns:xsd="http://www.w3.org/2001/XMLSchema"
   xmlns="http://www.w3.org/2001/XMLSchema">
 
   <complexType name="IntItem">
      <sequence>
         <element name="name" type="xsd:string"/>
         <element name="value" type="xsd:int"/>
      </sequence>
   </complexType>
   
   <complexType name="StringItem">
      <sequence>
         <element name="name" type="xsd:string"/>
         <element name="value" type="xsd:string"/>
      </sequence>
   </complexType>
</schema>

Java Custom Type Implementation

Example 7-9 illustrates an implementation of the Java custom type oracle.webservices.examples.customtypes.MyIntItem. This type will be used to represent the InitItem complexType illustrated in Example 7-8.

Example 7-9 Implementation of the Custom Java Type InitItem

/**
 * MyIntItem does not have the JAXRPC Value type (Bean) pattern.
 */
package oracle.webservices.examples.customtypes;
public class MyIntItem {    
    private String name;
    private int value;    
    public MyIntItem(String name, int value) {
        this.name = name;
        this.value = value;
    }    
    public String itemName() {
        return name;
    }    
    public int itemValue() {
        return value;
    }
}

Implementing a Serializer with Custom Marshalling Logic

This section provides examples of the files you must provide and the tasks you must perform if you want to implement a custom serializer that contains custom marshalling logic.

Defining a Serializer Implementation with Marshalling Logic

This section provides an example of a serializer implementation that contains custom marshalling and unmarshalling logic. It assumes that you want to use the oracle.webservices.examples.customtypes.MyInitItem custom Java data type in your Web service to represent the InitItem complex type in the schema document. It also assumes that you want to provide custom marshalling and unmarshaling logic for the class. To provide the custom logic, you must implement a custom serializer.

Example 7-10 provides a sample custom serializer implementation for MyInitItem. To provide serialization capabilities, the custom serializer, MyInitItemSerializer, implements the SOAPElementSerializer class and implements its serialize and deserialize methods.

Example 7-10 Custom Serializer Implementation to Map MyInitItem to InitItem

package oracle.webservices.examples.customtypes;
 
import java.util.Iterator;
import java.util.Map;
 
import javax.xml.namespace.QName;
import javax.xml.soap.SOAPElement;
import javax.xml.soap.SOAPException;
import javax.xml.soap.SOAPFactory;
import javax.xml.soap.Text;
 
import oracle.webservices.databinding.SOAPElementSerializer;
 
public class MyIntItemSerializer implements SOAPElementSerializer {
 
    private Map config;
    private SOAPFactory soapFactory;
    public void init(Map initParams) {
        config = initParams;
        try {
            soapFactory = SOAPFactory.newInstance();
        } catch (SOAPException e) {
            e.printStackTrace();
        }
    }
 
    public SOAPElement serialize(QName tagName, Object object) {
        MyIntItem myIntItem = (MyIntItem)object;
        SOAPElement xml = null;
        try {
            xml = soapFactory.createElement(
                  tagName.getLocalPart(), tagName.getPrefix(), tagName.getNamespaceURI());
 
            SOAPElement name = soapFactory.createElement(
                  "name", tagName.getPrefix(), tagName.getNamespaceURI());
            name.addTextNode(myIntItem.itemName());
 
            SOAPElement value = soapFactory.createElement(
                  "value", tagName.getPrefix(), tagName.getNamespaceURI());
            value.addTextNode(String.valueOf(myIntItem.itemValue()));
 
            xml.addChildElement(name);
            xml.addChildElement(value);
        } catch (SOAPException e) {
            e.printStackTrace();
        }
        return xml;
    }
 
    private String getTextContent(SOAPElement element) {
        String str = "";
        for (Iterator i = element.getChildElements(); i.hasNext();) {
            Object obj = i.next();
            if (obj instanceof Text) {
                str += (((Text) obj).getValue());
            }
        }
        return str;
    }
 
    public Object deserialize(SOAPElement element) {
        String name = null;
        int value = -1;
        for (Iterator i = element.getChildElements(); i.hasNext();) {
            Object obj = i.next();
            if (obj instanceof SOAPElement) {
                SOAPElement child = (SOAPElement)obj;
                if (child.getLocalName().equals("name")) {
                    name = MyStringSerializer.createMyString(getTextContent(child));
                }
                if (child.getLocalName().equals("value")) {
                    value = Integer.parseInt(getTextContent(child));
                }
            }
        }
        return new MyIntItem(name, value);
    }
} 

Example 7-11 provides a sample custom serialization implementation to map java.lang.String to xsd:string. To provide serialization capabilities, the custom serializer, MyStringSerializer, implements the SOAPElementSerializer class and implements its serialize and deserialize methods. It also provides a placeholder where you can enter your own string formatting or validation code.

Example 7-11 Custom Serializer Implementation to Map java.lang.String to xsd:string

package oracle.webservices.examples.customtypes;
 
import java.util.Iterator;
import java.util.Map;
 
import javax.xml.namespace.QName;
import javax.xml.soap.SOAPElement;
import javax.xml.soap.SOAPException;
import javax.xml.soap.SOAPFactory;
import javax.xml.soap.Text;
 
import oracle.webservices.databinding.SOAPElementSerializer;
 
public class MyStringSerializer implements SOAPElementSerializer {
    
    private Map config;
    private SOAPFactory soapFactory;
 
    public void init(Map initParams) {
        config = initParams;
        try {
            soapFactory = SOAPFactory.newInstance();
        } catch (SOAPException e) {
            e.printStackTrace();
        }
    }
 
    public SOAPElement serialize(QName tagName, Object object) {
        String value = (object == null) ? "null" : object.toString();
        SOAPElement xml = null;
        try {
            xml = soapFactory.createElement( tagName.getLocalPart(),
                                             tagName.getPrefix(),
                                             tagName.getNamespaceURI());
            xml.addTextNode(value);
        } catch (SOAPException e) {
            e.printStackTrace();
        }
        return xml;
    }
 
    public Object deserialize(SOAPElement element) {
        String value = "";
        for (Iterator i = element.getChildElements(); i.hasNext();) {
            Object obj = i.next();
            if (obj instanceof Text) {
                value += (((Text) obj).getValue());
            }
        }
        return createMyString( value );
    }
    
    static public String createMyString(String input) {
        // You can add your own formating or validation logic here
        return input.trim().toUpperCase();
    }
}

Developing a Service Endpoint Interface and Implementation

This section illustrates a service endpoint implementation that uses a different Java data type than the one in the generated schema document.

Example 7-14 provides a sample service endpoint interface, oracle.webservices.examples.customtypes.MyEndpointIntf, and Example 7-15 provides its implementation, oracle.webservices.examples.customtypes.MyEndpointImpl. Note that the interface extends java.rmi.Remote and its methods throw java.rmi.RemoteException. Also note that the getItems methods in the interface and implementation get items of type MyInitItem instead of the InitItem value type class generated from the schema document.

Example 7-12 illustrates the generated schema document that defines the Inititem and StringItem types.

Example 7-12 Schema Definitions of InitItem and StringItem

<?xml version = '1.0' encoding = 'UTF-8'?>
<schema
    targetNamespace="http://examples.webservices.oracle/customtypes"
    xmlns:tns="http://ws.oracle.com/types"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    xmlns="http://www.w3.org/2001/XMLSchema">
    <!-- this complex type is mapped to a custom value type using a custom serializer -->
    <complexType name="IntItem">
        <sequence>
            <element name="name" type="xsd:string"/>
            <element name="value" type="xsd:int"/>
        </sequence>
    </complexType>

    <!—xsd:string is mapped to a custom value type using a custom serializer -->
    <complexType name="StringItem">
        <sequence>
            <element name="name" type="xsd:string"/>
            <element name="value" type="xsd:string"/>
        </sequence>
    </complexType>
</schema>

To assemble a Web service that will expose the methods that use oracle.webservices.examples.customtypes.MyInitItem data types, you must also replace the InitItem entry in the oracle-webservices type mapping configuration XML file with MyInitItem. This is described in "Editing the Generated oracle-webservices Type Mapping Configuration XML File".

Optionally, if you want to provide custom marshalling and unmarshaling for MyInitItem data, you must implement a custom serializer for this class. This is described in "Defining a Serializer Implementation with Marshalling Logic".

Example 7-13 illustrates the definition of the Items custom type class. This class will be used in the service endpoint implementation. the items class uses two value types, MyIntItem and StringItem. The MyInitItem class is a custom class that you need to provide a custom serializer. The StringItem class is generated from the schema with custom mapping of String. Both MyIntItem and StringItem are described by an existing schema document.

Example 7-13 Definition of the Items Class

package oracle.webservices.examples.customtypes;
 
public class Items {
    private MyIntItem[]  myIntItem;
    private StringItem[] stringItem;    
    public Items() {
    }    
    public Items(MyIntItem[] intItem, StringItem[] strItem) {
        myIntItem  = intItem;
        stringItem = strItem;
    }
    public MyIntItem[] getMyIntItem() {
        return myIntItem;
    }
    public void setMyIntItem(MyIntItem[] intItem) {
        myIntItem = intItem;
    }
    public StringItem[] getStringItem() {
        return stringItem;
    }
    public void setStringItem(StringItem[] strItem) {
        stringItem = strItem;
    }
}
 

Example 7-14 illustrates the service endpoint interface for MyEndpointIntf. Notice that the getItems method uses the MyInitItem data type instead of the InitItem type defined by the schema. As required by JAX-RPC, the interface extends java.rmi.Remote and the methods throw java.rmi.RemoteException.

Example 7-14 Service Endpoint Interface for MyEndpointIntf

package oracle.webservices.examples.customtypes;
 
import java.rmi.Remote;
import java.rmi.RemoteException;
 
public interface MyEndpointIntf extends Remote {
    public Items getItems( MyIntItem[] myInts, StringItem[] myStrings ) 
        throws RemoteException;
}

Example 7-15 illustrates the implementation of the MyEndpointIntf interface.

Example 7-15 Service Endpoint Implementation

package oracle.webservices.examples.customtypes;
 
public class MyEndpointImpl {
    
    public Items getItems( MyIntItem[] myInts, StringItem[] myStrings ) {
        Items items = new Items();
        items.setMyIntItem(myInts);
        items.setStringItem(myStrings);
        return items;
    }
}

Editing the Generated oracle-webservices Type Mapping Configuration XML File

The genValueTypes command creates a generated instance of the oracle-webservices type mapping configuration XML file, custom-type-mappings.xml. This file records the Java value type classes that are generated from an existing schema document. When you generate a WSDL from a service endpoint interface, you can use this file as input to WebServicesAssembler to avoid regenerating the schema definition for these pre-generated classes.

If you want to use an alternative Java class to represent a complex type in the schema instead of the class generated from the schema document, then you must edit the contents of the appropriate <type-mapping> element in the custom type mapping file. You can also edit the element to include a custom serializer for the alternative class.

The sample custom type mapping file in Example 7-16 assumes that the genValueTypes command used the schema in Example 7-8 to generate the Java value types.

Example 7-16 Custom Type Mapping File, with Pre-Generated Value Type Classes

<?xml version="1.0" encoding="UTF-8"?>
    <oracle-webservices>
        <webservice-description name="unknown">
            <type-mappings
                xmlns:typens0="http://examples.webservices.oracle/customtypes"
                xmlns:typens2="http://www.w3.org/2001/XMLSchema"
                >
                <type-mapping
                    java-class="oracle.webservices.examples.customtypes.StringItem"
                    xml-type="typens0:StringItem">
                </type-mapping>
                <type-mapping
                    java-class="oracle.webservices.examples.customtypes.IntItem"
                    xml-type="typens0:IntItem">
                </type-mapping>
                <type-mapping
                    java-class="int"
                    xml-type="typens2:int">
                </type-mapping>
                <type-mapping
                    java-class="java.lang.String"
                    xml-type="typens2:string">
                </type-mapping>
            </type-mappings>
        </webservice-description>
    </oracle-webservices>

To use oracle.webservices.examples.customtypes.MyInitItem and its custom serializer oracle.webservices.examples.customtypes.MyInitItemSerializer instead of the generated InitItem class, and to use the custom serializer oracle.webservices.examples.customtypes.MyStringSerializer for java.lang.String, you must edit the custom-type-mappings.xml file.

  1. Replace the instance of InitItem.

    1. Replace the instance of InitItem with oracle.webservices.examples.customtypes.MyInitItem in the appropriate <type-mapping> element.

    2. Add a <serializer> element to <type-mapping> to specify the custom serializer class, oracle.webservices.examples.customtypes.MyInitItemSerializer for MyInitItem.

      For example, the original <type-mapping> element in custom-type-mappings.xml:

      <type-mapping
          java-class="oracle.webservices.examples.customtypes.IntItem"
          xml-type="typens0:IntItem">
      </type-mapping>
      
      

      should now look like this:

       <type-mapping
          java-class="oracle.webservices.examples.customtypes.MyIntItem"
          xml-type="typens0:IntItem">
          <serializer class="oracle.webservices.examples.customtypes.MyInitItemSerializer"/>
      </type-mapping>
      
      
  2. Edit the <type-mapping> element for the mapping of java.lang.String to xsd:string. The entry should look like this:

    <type-mapping> 
       java-class="java.lang.String"
       xml-type="xsd:string">
       <serializer class="oracle.webservices.examples.customtypes.MyStringSerializer"/>
    </type-mapping>
    
    
  3. Rename the file MyCustomTypeMappings.xml and copy it to the root directory of the project.

Example 7-17 illustrates the edited version of the type mapping file. The type-mapping element for StringItem is retained in the file in order to avoid regenerating the schema definition for this pre-generated class.

The namespace prefix declaration (xmlns:xsd="http://www3w.org/2001/XMLSchema") is added so that it can be used in the type mapping configuration file. In the edited type mapping configuration file, you must refer to the string schema type defined in the schema namespace (http://www3w.org/2001/XMLSchema). Defining the prefix, xsd, enables you to use xsd:string as the "qualified name" of the schema type string.

Example 7-17 Custom Type Mapping File, with Edited Value Type Classes

<?xml version="1.0" encoding="UTF-8"?>
<oracle-webservices>
    <webservice-description name="unknown">
        <type-mappings
            xmlns:typens0="http://examples.webservices.oracle/customtypes"
            xmlns:xsd="http://www.w3.org/2001/XMLSchema">
            <type-mapping
                java-class="java.lang.String"
                xml-type="xsd:string">
                <serializer class="oracle.webservices.examples.customtypes.MyStringSerializer"/>
            </type-mapping>
            <type-mapping
                java-class="oracle.webservices.examples.customtypes.MyIntItem"
                xml-type="typens0:IntItem">
                <serializer class="oracle.webservices.examples.customtypes.MyIntItemSerializer"/>
            </type-mapping>
            <type-mapping
                java-class="oracle.webservices.examples.customtypes.StringItem"
                xml-type="typens0:StringItem">
            </type-mapping>
        </type-mappings>
    </webservice-description>
</oracle-webservices>

Developing a Client for Custom Type Mapping and a Custom Serializer

To generate J2SE client-side proxy files, use the WebServicesAssembler genProxy command:

java -jar wsa.jar -genProxy
     -wsdl http://localhost:8888/MyCustomTypes/MyCustomTypes?WSDL
     -output ./build/src/client
     

Because WebServicesAssembler does not configure custom types when generating the Web services client-side proxy, the generated proxy will try to generate Value Type (Bean) classes by default to represent the schema types InitItem and StringItem. Then you can write your client program using the generated proxy code.

Example 7-18 Client Program Using Default Generated Value Types

package oracle.webservices.examples.customtypes;
    import javax.xml.rpc.ServiceFactory;
    import javax.xml.rpc.Stub;
    import examples.webservices.oracle.customtypes.*;
    public class MyCustomTypesClient {
        public static final String DEFAULT_URL
 ="http://localhost:8888/MyCustomTypes/MyCustomTypes";
    
        public static void main(String[] args) throws Exception {
            String address = DEFAULT_URL;
            ServiceFactory factory = ServiceFactory.newInstance();
            MyCustomTypes service = (MyCustomTypes) factory.loadService(MyCustomTypes.class);
            MyEndpointIntf proxy = (MyEndpointIntf)service.getPort(MyEndpointIntf.class);
            ((Stub) proxy)._setProperty(Stub.ENDPOINT_ADDRESS_PROPERTY, address);
            IntItem int1 = new IntItem();
            int1.setName("int1");
            int1.setValue(123);
            IntItem int2 = new IntItem();
            int2.setName("int2");
            int2.setValue(456);
            IntItem[] ints = { int1, int2 };
            StringItem str1 = new StringItem();
            str1.setName("string1");
            str1.setValue("foo");
            StringItem str2 = new StringItem();
            str2.setName("string2");
            str2.setValue("coo");
            StringItem[] strings = { str1, str2 };
            Items items = proxy.getItems(ints, strings);
            System.out.println("items.getMyIntItem : " + items.getMyIntItem().length);
            System.out.println("items.getStringItem: " + items.getStringItem().            length);
        }
}

The MyInitItem class was used in developing the Items class aforementioned. The Items type was developed to be a return value of the service endpoint implementation. The MyInitItemSerializer and MyStringTypeSerializer are used in the OracleAS Web Services runtime. While WebServicesAssembler is assembling the deployable EAR file, the edited Type Mapping Configuration XML File is used to create a deployment descriptor inside the ear so that these custom serializes will be correctly used in the runtime.

How to Use Custom Serializers in Client Code

You can use a custom serializer in the Web service client-side proxy. This enables you to use the custom data types defined for the service in your client code. This section describes how to create a Web service client-side proxy that uses the MyIntItemSerializer (described in Example 7-10) to map IntItem to MyIntItem and MyStringSerializer (described in Example 7-11) to map the string type. To do this, you must use a client side version of the oracle-webservices type mapping configuration file, MyCustomTypeMappings.xml (described in Example 7-17) as input to genProxy.

The following steps summarize the process of adding a custom serializer to a client proxy:

  1. Provide a client version of the server-side custom type mapping file.

    For more information on this step, see "Editing the Server Side Custom Type Mapping File".

  2. Generate the Web service client proxy with the WebServicesAssembler genProxy command.

    For more information on this step, see "Generating the Web Service Client Side Proxy".

  3. Write the Web service client.

    For more information on this step, see "Writing a Web Service Client with Custom Datatypes".

Editing the Server Side Custom Type Mapping File

The custom type mapping file used on the client side must conform to the oracle-webservices-client-10_0.xsd schema. To provide a custom type mapping file for the client, you can use the service side custom type mapping file as a template. Edit the file to provide the appropriate client side root elements:

  • Replace the <oracle-webservices> element with <oracle-webservices-clients>.

  • Replace the <webservice-description> element and any attributes it might contain with <webservice-client>.

After making these edits, the client side custom type mapping file (in this case, MyCustomTypeMappings.xml) should look like the code in Example 7-19.

Example 7-19 Client Side Custom Type Mapping File

<?xml version="1.0" encoding="UTF-8"?>
<oracle-webservice-clients>
   <webservice-client>
      <type-mappings xmlns:typens0="http://examples.webservices.oracle/customtypes" 
             xmlns:xsd="http://www.w3.org/2001/XMLSchema">
          <type-mapping 
              java-class="java.lang.String" 
              xml-type="xsd:string">
              <serializer class="oracle.webservices.examples.customtypes.MyStringSerializer"/>
          </type-mapping>
          <type-mapping 
              java-class="oracle.webservices.examples.customtypes.MyIntItem"
              xml-type="typens0:IntItem">
             <serializer class="oracle.webservices.examples.customtypes.MyIntItemSerializer"/>
          </type-mapping>
          <type-mapping 
              java-class="oracle.webservices.examples.customtypes.StringItem" 
              xml-type="typens0:StringItem">
          </type-mapping>
       </type-mappings>
   </webservice-client>
</oracle-webservice-clients> 
Generating the Web Service Client Side Proxy

Generate the Web services client-side proxy with the WebServicesAssembler genProxy command. Use the ddFileName argument to provide the client side custom type mapping file as input. The genProxy command will create client proxy classes will use your serializers.

The following sample genProxy command uses the client side custom type mapping file MyCustomTypeMappings.xml as input.

java -jar wsa.jar -genProxy
     -wsdl http://localhost:8888/MyCustomTypes/MyCustomTypes?WSDL
     -ddFileName ./MyCustomTypeMappings.xml
     -output ./build/src/client
Writing a Web Service Client with Custom Datatypes

When you write your client program using the generated proxy code, you can call MyIntItem instead of IntItem. This is illustrates in Example 7-20.

Example 7-20 Client Code Incorporating Custom Type Mapping and Custom Serializer

ServiceFactory factory = ServiceFactory.newInstance();
MyCustomTypes service = (MyCustomTypes) factory.loadService(MyCustomTypes.class);
MyEndpointIntf proxy = (MyEndpointIntf)service.getPort(MyEndpointIntf.class);
((Stub) proxy)._setProperty(Stub.ENDPOINT_ADDRESS_PROPERTY, address);

    MyIntItem int1 = new MyIntItem("int1", 123);
    MyIntItem int2 = new MyIntItem("int2", 456);
    MyIntItem [] ints = { int1, int2 };

    StringItem str1 = new StringItem();
    str1.setName("string1");
    str1.setValue("foo");
    StringItem str2 = new StringItem();
    str2.setName("string2");
    str2.setValue("coo");
    StringItem[] strings = { str1, str2 };

    Items items = proxy.getItems(ints, strings);

Limitations

See "Custom Serialization of Java Value Types".

Additional Information

For more information on: