bea.com | products | dev2dev | support | askBEA

Download Docs | Site Map | Glossary
Search

Programming WebLogic Web Services

Using Non-Built-In Data Types

The following sections describe how to use non-built-in data types in WebLogic Web services:

- Overview of Using Non-Built-In Data Types

- Creating Non-Built-In Data Types Manually: Main Steps

Overview of Using Non-Built-In Data Types

You can create a WebLogic Web service that uses non-built-in data types as the Web service parameters and return value. Non-built-in data types are defined as data types other than the supported built-in data types, such as int and String. For the full list of built-in types, see Using Built-In Data Types.

WebLogic Server transparently handles the conversion of the built-in data types between their XML and Java representation. However, if your Web service operation uses non-built-in data types, you must provide the following information so that WebLogic Server can perform the conversion:

- Serialization class that converts between the XML and Java representation of the data.

- A Java class to contain the Java representation of the data type.

- An XML Schema representation of the data type.

- Data type mapping information in the web-services.xml deployment descriptor file.

WebLogic Server includes the servicegen and autotype Ant tasks which automatically generate the preceding components by introspecting the stateless session EJB or Java class backend component for your Web service. These Ant tasks can handle many non-built-in data types, so most programmers will not ever have to create the components manually.

Sometimes, however, you may need to create the non-built-in data type components manually. Your data type may be so complex that the Ant task cannot correctly generate the components. Or maybe you want more control over how the data is converted between its XML and Java representations rather than relying on the default conversion procedure used by WebLogic Server.

For a full list of the supported non-built-in data types, see Non-Built-In Data Types Supported by servicegen and autotype Ant Tasks.

For procedural instructions on using servicegen and autotype, see Assembling WebLogic Web Services Using Ant Tasks. For reference information, see Web Service Ant Tasks and Command-Line Utilities.

Creating Non-Built-In Data Types Manually: Main Steps

The following procedure describes how to create non-built-in data types and use the servicegen Ant task to create a deployable Web service:

1. Write the XML Schema representation of your data type. See Writing the XML Schema Data Type Representation.

2. Write a Java class that represents your data type. See Writing the Java Data Type Representation.

3. Write a serialization class that converts the data between its XML and Java representations. See Writing the Serialization Class.

4. Compile your Java code into classes. Ensure that your CLASSPATH variable can locate the classes.

5. Create a text file that contains the data type mapping information about your non-built-in data type. See Creating the Data Type Mapping File.

6. Assemble your Web service using the servicegen Ant task as described in Assembling WebLogic Web Services Using the servicegen Ant task, with the following addition: when creating the build.xml file that calls the servicegen Ant task, be sure you specify the typeMappingFile attribute of servicegen, setting it equal to the name of the data type mapping file you created in the preceding step.

   BEA recommends that you create an exploded directory, rather than an EAR file, by specifying a value for the destEar attribute of servicegen that does not have an .ear suffix. You can later package the exploded directory into an EAR file when you are ready to deploy the Web service.

7. Update the web-services.xml file (which was generated by the servicegen Ant task), adding the XML Schema representation of your data type that you created in the first step of this procedure. See Updating the web-services.xml File With XML Schema Information.

8. Either deploy the exploded directory as your Web service, or package the directory into an EAR file and deploy it on WebLogic Server.

9. If you want to use the clientgen Ant task to generate a Java client, follow the procedure described in Running the clientgen Ant Task with the following additions to the build.xml file that calls clientgen:

   - Specify the ear attribute and set it to the full name of your Web service EAR file. Do not specify the wsdl attribute.

   - Specify the useServerTypes attribute and set it to True.

Writing the XML Schema Data Type Representation

Web services use SOAP as the message format to transmit data between the service and the client application that invokes the service. Because SOAP is an XML-based protocol, you must use XML Schema notation to describe the structure of non-built-in data types used by Web service operations.

Warning: XML Schema is a powerful and complex data description language, and its use is not recommended for the faint of heart.

The following example shows the XML Schema that describes a non-built-in data type called EmployBean:

>     <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
>                 xmlns:stns="java:examples.newTypes"
>                 attributeFormDefault="qualified"
>                 elementFormDefault="qualified"
>                 targetNamespace="java:examples.newTypes">
>        <xsd:complexType name="EmployeeBean">
>           <xsd:sequence>
>             <xsd:element name="name"
>                          type="xsd:string"
>                          nillable="true"
>                          minOccurs="1"
>                          maxOccurs="1">
>             </xsd:element>
>             <xsd:element name="id"
>                          type="xsd:int"
>                          minOccurs="1"
>                          maxOccurs="1">
>             </xsd:element>
>           </xsd:sequence>
>        </xsd:complexType>
>     </xsd:schema>

The following XML shows an instance of the EmployeeBean data type:

>     <EmployeeBean>
>       <name>Beverley Talbott</name>
>       <id>1234</id>
>     </EmployeeBean>

For detailed information about using XML Schema notation to describe your non-built-in data type, see the XML Schema specification.

Writing the Java Data Type Representation

You use the Java representation of the non-built-in data type in your EJB or Java class that implements the Web service operation.

The following example shows one possible Java representation of the EmployeeBean data type whose XML representation is described in the preceding section:

>     package examples.newTypes;

>     /**
>      * @author Copyright (c) 2002 by BEA Systems. All Rights Reserved.
>      */

>     public final class EmployeeBean {

>       private String name = "John Doe";
>       private int id = -1;

>       public EmployeeBean() {
>       }

>       public EmployeeBean(String n, int i) {
>         name = n;
>         id = i;
>       }

>       public String getName() {
>         return name;
>       }
>       public void setName(String  v) {
>         this.name = v;
>       }

>       public int getId() {
>         return id;
>       }
>       public void setId(int  v) {
>         this.id = v;
>       }

>       public boolean equals(Object obj) {
>         if (obj instanceof EmployeeBean) {
>           EmployeeBean e = (EmployeeBean) obj;
>           return (e.name.equals(name) && (e.id == id));
>         }
>         return false;
>       }
>     }

Writing the Serialization Class

The serialization class performs the actual conversion of your data between its XML and Java representations. You write only one class that contains methods to serialize and deserialize your data. In the class you use the WebLogic XML Streaming API to process the XML data.

The WebLogic XML Streaming API provides an easy and intuitive way to consume and generate XML documents. It enables a procedural, stream-based handling of XML documents.

For detailed information on using the WebLogic XML Streaming API, see Programming WebLogic XML.

The following example shows a class that uses the XML Streaming API to serialize and deserialize the data type described in Writing the XML Schema Data Type Representation and Writing the Java Data Type Representation; the procedure after the example lists the main steps to create such a class:

>     package examples.newTypes;

>     import weblogic.webservice.encoding.AbstractCodec;

>     import weblogic.xml.schema.binding.DeserializationContext;
>     import weblogic.xml.schema.binding.DeserializationException;
>     import weblogic.xml.schema.binding.Deserializer;
>     import weblogic.xml.schema.binding.SerializationContext;
>     import weblogic.xml.schema.binding.SerializationException;
>     import weblogic.xml.schema.binding.Serializer;

>     import weblogic.xml.stream.Attribute;
>     import weblogic.xml.stream.CharacterData;
>     import weblogic.xml.stream.ElementFactory;
>     import weblogic.xml.stream.EndElement;
>     import weblogic.xml.stream.StartElement;
>     import weblogic.xml.stream.XMLEvent;
>     import weblogic.xml.stream.XMLInputStream;
>     import weblogic.xml.stream.XMLName;
>     import weblogic.xml.stream.XMLOutputStream;
>     import weblogic.xml.stream.XMLStreamException;

>     public final class EmployeeBeanCodec extends
>       weblogic.webservice.encoding.AbstractCodec
>     {
>       public void serialize(Object obj,
>                             XMLName name,
>                             XMLOutputStream writer,
>                             SerializationContext context)
>         throws SerializationException
>       {
>         EmployeeBean emp = (EmployeeBean) obj;

>         try {

>           //outer start element
>           writer.add(ElementFactory.createStartElement(name));

>           //employee name element
>           writer.add(ElementFactory.createStartElement("name"));
>           writer.add(ElementFactory.createCharacterData(emp.getName()));
>           writer.add(ElementFactory.createEndElement("name"));

>           //employee id element
>           writer.add(ElementFactory.createStartElement("id"));
>           String id_string = Integer.toString(emp.getId());
>           writer.add(ElementFactory.createCharacterData(id_string));
>           writer.add(ElementFactory.createEndElement("id"));

>           //outer end element
>           writer.add(ElementFactory.createEndElement(name));

>         } catch(XMLStreamException xse) {
>           throw new SerializationException("stream error", xse);
>         }
>       }

>       public Object deserialize(XMLName name,
>                                 XMLInputStream reader,
>                                 DeserializationContext context)
>         throws DeserializationException
>       {
>         // extract the desired information out of reader, consuming the
>         // entire element representing the type,
>         // construct your object, and return it.
>         EmployeeBean employee = new EmployeeBean();

>         try {
>           if (reader.skip(name, XMLEvent.START_ELEMENT)) {
>             StartElement top = (StartElement)reader.next();

>             //next start element should be the employee name
>             if (reader.skip(XMLEvent.START_ELEMENT)) {
>               StartElement emp_name = (StartElement)reader.next();

>               //assume that the next element is our name character data
>               CharacterData cdata = (CharacterData) reader.next();
>               employee.setName(cdata.getContent());
>             } else {
>               throw new DeserializationException("employee name not found");
>             }

>             //next start element should be the employee id
>             if (reader.skip(XMLEvent.START_ELEMENT)) {
>               StartElement emp_id = (StartElement)reader.next();

>               //assume that the next element is our id character data
>               CharacterData cdata = (CharacterData) reader.next();
>               employee.setId(Integer.parseInt(cdata.getContent()));
>             } else {
>               throw new DeserializationException("employee id not found");
>             }

>             //we must consume our entire element to leave the stream in a
>             //good state for any other deserializer
>             if (reader.skip(name, XMLEvent.END_ELEMENT)) {
>               XMLEvent end = reader.next();
>             } else {
>               throw new DeserializationException("expected end element not found");
>             }
>           } else {
>             throw new DeserializationException("expected start element not found");
>           }
>         } catch (XMLStreamException xse) {
>           throw new DeserializationException("stream error", xse);
>         }
>         return employee;
>       }

>       public Object deserialize(XMLName name,
>                                 Attribute att,
>                                 DeserializationContext context)
>         throws DeserializationException
>       {
>         //NOTE: not used in this example

>         // extract the desired information out of att, consuming the
>         // entire element representing the type,
>         // construct your object, and return it.
>         return new EmployeeBean();
>       }
>     }

To create the serialization class using the WebLogic XML Streaming API, follow these steps:

1. Import the following classes, which are implemented by the abstract class that your serialization class will extend:

   >     import weblogic.xml.schema.binding.DeserializationContext;
   >     import weblogic.xml.schema.binding.DeserializationException;
   >     import weblogic.xml.schema.binding.Deserializer;
   >     import weblogic.xml.schema.binding.SerializationContext;
   >     import weblogic.xml.schema.binding.SerializationException;
   >     import weblogic.xml.schema.binding.Serializer;

2. Import the WebLogic XML Streaming API classes as needed. The preceding example imports the following classes:

   >     import weblogic.xml.stream.Attribute;
   >     import weblogic.xml.stream.CharacterData;
   >     import weblogic.xml.stream.ElementFactory;
   >     import weblogic.xml.stream.EndElement;
   >     import weblogic.xml.stream.StartElement;
   >     import weblogic.xml.stream.XMLEvent;
   >     import weblogic.xml.stream.XMLInputStream;
   >     import weblogic.xml.stream.XMLName;
   >     import weblogic.xml.stream.XMLOutputStream;
   >     import weblogic.xml.stream.XMLStreamException;

3. Write your Java class to extend the following abstract class:

   >     weblogic.webservice.encoding.AbstractCodec

4. Implement the serialize() method, used to convert the data from Java to XML. The signature of this method is as follows:

   >       void serialize(Object obj,
   >                      XMLName name,
   >                      XMLOutputStream writer,
   >                      SerializationContext context)
   >         throws SerializationException;

   Your Java object will be contained in the Object parameter. Use the XML Streaming API to write the Java object to the XMLOutputStream parameter. Use the XMLName parameter as the name of the resulting element.

   Warning: Do not update the SerializationContext parameter; it is used internally by WebLogic Server.

5. Implement the deserialize() method, used to convert the data from XML to Java. The signature of this method is as follows:

   >       Object deserialize(XMLName name,
   >                          XMLInputStream reader,
   >                          DeserializationContext context)
   >         throws DeserializationException;

   The XML that you want to deserialize is contained in the XMLInputStream parameter. Use the WebLogic XML Streaming API to parse the XML and convert it into the returned Object. The XMLName parameter contains the expected name of the XML element.

   Call the deserialize() method recursively to build contained Objects.

   When you use the XML Streaming API to read the stream of events that make up your XML document, be sure you always finish reading an element all the way up to and including the EndElement event, rather than finish reading once you have read all the actual data. If you finish before reaching an EndElement event, the deserialization of subsequent elements might fail.

   Warning: Do not update the DeserializationContext parameter; it is used internally by WebLogic Server.

6. If the data type for which you are creating a serialization class is used as an attribute value in your XML files, implement the following variation of the deserialize() method:

   >       Object deserialize(XMLName name,
   >                          Attribute att,
   >                          DeserializationContext context)
   >         throws DeserializationException;

   The Attribute parameter contains the attribute value to deserialize. The XMLName attribute contains the expected name of the XML element.

   Warning: Do not update the DeserializationContext parameter; it is used internally by WebLogic Server.

Creating the Data Type Mapping File

The data type mapping file is a subset of the web-services.xml deployment descriptor file. It centralizes some of the information about non-built-in data types, such as the name of the Java class that describes the Java representation of the data, the name of the serialization class that converts the data between XML and Java, and so on. The servicegen Ant task uses this data type mapping file when creating the web-services.xml deployment descriptor for the WebLogic Web service that uses the non-built-in data type.

To create the data type mapping file, follow these steps:

1. Create a text file with any name.

2. Within in the text file, add a <type-mapping> root element:

   >     <type-mapping>
   >     ...
   >     </type-mapping>

3. For each non-built-in data type for which you have created a serialization class, add a <type-mapping-entry> child element of the <type-mapping> element. Include the following attributes:

   - xmlns:name —Declares a namespace.

   - class-name—Specifies the fully qualified name of the Java class.

   - type—Specifies the name of XML Schema type for which this data type mapping entry applies.

   - serializer—The fully qualified name of the serialization class that converts the data from its Java to its XML representation. For details on creating this class, see Writing the Serialization Class.

   - deserializer—The fully qualified name of the serialization class that converts the data from its XML to its Java representation. For details on creating this class, see Writing the Serialization Class.

   The following example shows a possible data type mapping file with one <type-mapping> entry for the XML Schema data type shown in Updating the web-services.xml File With XML Schema Information:

>     <type-mapping>
>         <type-mapping-entry
>             xmlns:p2="java:examples.newTypes"
>             class-name="examples.newTypes.EmployeeBean"
>             type="p2:EmployeeBean"
>             serializer="examples.newTypes.EmployeeBeanCodec">
>             deserializer="examples.newTypes.EmployeeBeanCodec"
>         </type-mapping-entry>
>     </type-mapping>

Updating the web-services.xml File With XML Schema Information

The web-services.xml file generated by servicegen will not have the XML Schema information for the non-built-in data type for which you have created your own custom serialization class. For this reason, you must manually add the XML Schema information to the deployment descriptor, as described in the following steps:

1. In the existing web-services.xml file generated by the servicegen Ant task, find the <types> child element of the <web-service> element:

   >     <types>
   >     ...
   >     </types>

2. Merge your XML Schema representation of your non-built-in data type that you created in Writing the XML Schema Data Type Representation with the any existing information within the <types> element, as shown in the following example:

   >     <types>
   >       <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
   >                   xmlns:stns="java:examples.newTypes"
   >                   attributeFormDefault="qualified"
   >                   elementFormDefault="qualified"
   >                   targetNamespace="java:examples.newTypes">
   >          <xsd:complexType name="EmployeeBean">
   >             <xsd:sequence>
   >               <xsd:element name="name"
   >                            type="xsd:string"
   >                            nillable="true"
   >                            minOccurs="1"
   >                            maxOccurs="1">
   >               </xsd:element>
   >               <xsd:element name="id"
   >                            type="xsd:int"
   >                            minOccurs="1"
   >                            maxOccurs="1">
   >               </xsd:element>
   >             </xsd:sequence>
   >          </xsd:complexType>
   >       </xsd:schema>
   >     </types>