Chapter 7 Selecting an EAServer Component for Your Web Service
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.
Throughout this document, "serializable" means
that a datatype is understood and available to the current instance
of the SoapMap.
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.
The BeanSerializer makes no guarantees
about the order in which members are serialized.
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:
com.sybase.webservices.soap.encoding.
BeanSerializer
com.sybase.webservices.soap.encoding.
BeanSerializer
http://schemas.xmlsoap.org/soap/
encoding/
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.
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>
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"; }}
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. }
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. |
![]() |