Chapter 7 Selecting an EAServer Component for Your Web Service


User-defined datatypes

One of the significant aspects of SOAP is its inherent flexibility in handling datatypes other than those defined in the SOAP or XML specifications. Theoretically, any object or datatype that can be described by a valid snippet of XML can be used as a parameter within a SOAP call. However, in practical terms there may be some limitations based on the underlying activation model or language.

Note   Throughout this document, "serializable" means that a datatype is understood and available to the current instance of the SoapMap.

BeanSerializer class

Web Services Toolkit provides a BeanSerializer class that is capable of serializing and deserializing Java objects that meet some of the basic criteria for the JavaBeans design pattern. The object must implement a public
no-argument constructor, as well as get and set methods for all properties, and the properties themselves must be serializable datatypes. This is not a part of the JavaBeans design pattern but is a requirement of the BeanSerializer. The JavaBeans design pattern also expects the object to implement the java.io.Serializable interface. This is not a requirement for the BeanSerializer; however, as a practical matter, the object should implement this interface if the EAServer component is a Java-CORBA component.

The BeanSerializer works by using Java's reflection capabilities to get and set values for the properties in the object. The underlying encoding pattern is the SOAP Struct pattern, which is based on the concept that members of a compound value are uniquely identifiable by name.

Note   The BeanSerializer makes no guarantees about the order in which members are serialized.

Associating a class with the BeanSerializer

To associate a class with the BeanSerializer using the Web Services Toolkit GUI, follow the steps in "Registering a user-defined datatype".

Enter the following values in the specified fields:

The next time you start the Webservices Web application or the EAServer, the encoding style declaration will be available. The user.encoding.props file that contains this declaration, in addition to the class, must be made available on the client side as well, so the client will be able to use the declared datatype. The file is in the Webservices directory in your EAServer installation, and also goes in the Webservices directory in the client installation.

Creating a serializer and deserializer for a datatype

There might be instances where the existing serializers and deserializers provided with the Web Services Toolkit will not be adequate to expose a class through SOAP. In this case, you need to create custom serializer and deserializer classes to perform the necessary actions to convert the class to and from XML.

This section assumes that you have a good understanding of both the SOAP and XML specifications. For this section, you will be using the following SimpleClass example to illustrate various aspects of creating a serializer and deserializer for a user-defined datatype.

package com.mycompany.mypackage;
public  class SimpleClass { 
     // 
     public  String  myString = ""; 
     public  int myInt = 0; 

     // class  business logic below... 
     // Sybase  assumes here that you do not have gets and
     // sets  or you would use the BeanSerializer 
} 

Because this SimpleClass is not a directly supported datatype and does not conform to the requirements of the BeanSerializer, you need to define a class that implements the serializer and deserializer interfaces in order to expose the SimpleClass through SOAP.

In general, the only restriction on how a class is serialized is that the serialized form must be well-formed XML. In practical terms, you might find it easiest to create a serializer that conforms to the SOAP encoding style so you can reuse existing serializers when possible. Javadoc for the classes and methods used in the following sections are available in the Webservices\docs\api directory in your EAServer installation.

Once you have created the class, you need to define a schema that describes the class. This document assumes that you are familiar with the XML Schema specification and able to create a valid schema for your datatype. For further information on XML schemas, refer to the XML Schema Part 0: Primer or the W3C Schema Pages for numerous specifications, references, tutorials and links.

For the SimpleClass, the following schema snippet describes the class.

<xsd:element name="SimpleClass"  type="SimpleClassType"/>
<xsd:complexType  name="SimpleClassType">
     <xsd:sequence>
          <xsd:element  name="myString" type="xsd:string"/>
          <xsd:element  name="myInt" type="xsd:int"/>
     </xsd:sequence>
</xsd:complexType>

Implementing the serializer

The purpose of the serializer is to convert the data elements in a class to an appropriate XML representation, as shown in the following example code.

package com.mycompany.mypackage;
// If  you are going to use the Sybase SOAP serialization classes import  them.
// You will also need the SoapException  class.
import com.sybase.webservices.soap.encoding.*;
import  com.sybase.webservices.soap.util.SoapException;

public  class SimpleClassSerializer 
     implements  Serializer
{ 
     /*
     *     Marshal the SimpleClass into well-formed XML
     */
// The  list of available encoding styles
     public  void marshal(String encodingStyleList,
// The  class of the type to be serialized
          Class  varJavaType
//The object to be serialized
          Object  varValue,
// The name of the parameter  being serialized
          Object  varName,
// The writer to write the XML  text into
          Writer  messageWriter, 
// The list of available  namespaces
          Vector  namespaceList, 
// The current SoapMap  throws SerializerException
          SoapMap  soapMap )
     throws  SerializerException
   {
// Determine  if the varValue object is null.
     boolean  isNull=theValue==null?true:false;

// Use  the SoapEncodingUtils.writeXMLElementHeader()
//static  method to create the opening stanza of the Element.
//This  method creates an entry of the form:
//
//    <varName xsi:type="xsdn:  com.mycompany.mypackage.
//                      SimpleClass"
//            xmlns:xsdn="the  namepspace">
//
// Note  that the namespace declaration is only included if the
//namespace  is not in the current namespaceList. The trailing n in
//the  xsdn indicates some index (for example, xsd1,  xsd2, and so on)
//depending on the number  of namespaces declared in the SoapMap. You
//cannot  assume any particular number.
     SoapEncodingUtils.writeXMLElementHeader( encodingStyle, 
               varJavaType,
               varName,
               messageWriter,
               namespaceList,
               soapMap,
// null  is used by the ArraySerializer
               null,
// null  is used by the ArraySerializer
               null,
               isNull  );

// Use the existing serializers  to serialize the
//individual elements  of the class.
// The SoapMap object will  return a serializer for the
//element,  if one is available.
// This will throw  a SoapException that must be caught and
//rethrown  as a SerializerException.
     try {
          Serializer  s = soapMap.querySerializer( myString.
                          getClass(  ), encodingStyle );

     // After  getting the serializer, use it to marshal
     //the  first element in SimpleClass.
          s.marshal(   encodingStyle,
            myString.getClass(  ),
            myString,
            "myString",
            messageWriter,
            namespaceList,
            soapMap  );

     // Repeat  the procedure for the remaining elements in
     //the  class to be serialized.
          s = soapMap.querySerializer(  int.class, encodingStyle );
          s.marshal( encodingStyle,
             int.class,
             myInt,
             "myInt",
             messageWriter,
             namespaceList,
             soapMap  );

     // Close  the element.
        messageWriter.write(  "</" + varName + ">"  );

   } catch  ( com.sybase.webservices.soap.util.
          SoapException  se ) {
        throw  new SerializerException( se.getMessage() );
     }
   }

   // implement  the toString( ) method, just return the
   //fully  qualified class name for this class
   public  String toString( )
   {
     return  "com.mycompany.mypackage.
             SimpleClassSerializer";
   }}

Implementing the deserializer

The purpose of the deserializer is to extract the data from an XML element and place it into the object. The following example adds the deserializer methods to the SimpleClassSerializer.

package com.mycompany.mypackage;
// If  you are going to use the Sybase SOAP serialization
//classes  import them.
// You will also need the  SoapException class.
import com.sybase.webservices.soap.encoding.*;
import  com.sybase.webservices.soap.util.SoapException;
// Import  the QName and DOM Element classes for
//unmarshalling.
import  com.sybase.webservices.util.QName;
import org.w3c.dom.Element;

public  class SimpleClassSerializer
   implements  Serializer, Deserializer
{ 
   // Serializer  code omitted...

   // The  unmarshal method extracts the data represented
   // as  a string in the XML and converts it into
   // the  correct form for the datatype receiving the data.
   // In  this example the child elements of the SimpleClass,
   // the  myString and myInt fields, are both types that
   // are  understood by the existing deserializers and so
   // they  can used to extract the data from the child
   //elements  and restrict the SimpleClassSerializer's
   //unmarshalling  tasks to getting the resulting data and
   //placing  it in the fields as appropriate.
   public  Bean unmarshal( String encodingStyle,
                           QName  qualifiedType,
                           Element  src,
                            SoapMap  map )
        throws  DeserializerException
   {
        // Create  a new instance of the class being
        //deserialized.
        SimpleClass  sc = new SimpleClass( );

        String  theName = src.getTagName( );

        try {
            // Get  the first child element of the current
            //element  and determine its type.

            Element  childElement = WebServicesParser.
                              getFirstChildElement(  src );
            QName  tempItemType = SoapEncodingUtils.
                        getAttributeValue(  childElement,
                         SoapConstants.URI_XML_XSI,
                        SoapConstants.ATTR_TYPE_PREFIX,
                         "",  false );
          // Get  the Deserializer for the first child
          //element.
          // In  the case of SimpleClass the
          // first  child element is the myString element.
          // However  if the order is indeterminate then 
          // find  a way to resolve which
          // element  is being deserialized.
          Deserializer  d = map.queryDeserializer(
                           tempItemType,  encodingStyle );

          Bean  b1 = d.unmarshal(  encodingStyle,
                                  tempItemType,
                                  childElement,  map );

          // Take  the value from the Bean and place it into
          //the  SimpleClass object
          sc.myString = b1.getValue(  );

          childElement =   WebServicesParser.
                          getNextSiblingElement
                           (  childElement );
          tempItemType = SoapEncodingUtils.
                          getAttributeValue(  childElement,
                               SoapConstants.URI_XML_XSI,
                               SoapConstants.
                               ATTR_TYPE_PREFIX,  "",
                               false  );
          // Get  the Deserializer for the next child
          // element.
          // In  the case of SimpleClass the
          // next  child element is the "myInt" element
          // However  if the order is indeterminate then 
          // find  a way to resolve which
          / /element  is being deserialized.

          d = map.queryDeserializer(  tempItemType,
                                      encodingStyle  );

          Bean  b2 = d.unmarshal(  encodingStyle,
                                  tempItemType,
                                  childElement,
                                   map  );

          sc.myInt = b2.getValue(  );
     } catch  (SoapException se) {
          throw  new DeserializerException( se.
                                           getMessage());
     }
   }

   // The  setValueAsOutParameter method is used to set the
   //values  extracted from the XML while unmarshalling and
   //place  them into the actual inout or out parameter
   //object  so that the values will be available to the
   //caller  upon return from the method invocation.
   public  void setValueAsOutParameter(   Object
                                     destObject,Class
                                     expectedClass,
                                       Object  value )
   {
     // Get  the element values from the value object and
     //assign  them to the destination object. 
     destObject.myString = value.myString;
     destObject.myInt = value.myInt;
   }

   // Reuse  the toString( ) method defined above in
   //the  Serializer section.
}

Associating a class with its serializer and deserializer

Follow the steps in "Registering a user-defined datatype".

Enter the following values in the specified fields:

The next time you start the Webservices Web application or the server, the endcoding style declaration will be available. The user.encoding.props file that contains this declaration, in addition to the class, must be made available on the client side so the client will be able to use the declared datatype. The file is in the Webservices directory in your EAServer installation, and goes in the Webservices directory in the client installation.

 


Copyright © 2002 Sybase, Inc. All rights reserved.