Conversion between Java objects at the application level and XML messages is performed through serializers and deserializers. Serializers convert Java objects to XML, while deserializers convert XML to Java objects. (The term "serialization" is sometimes used for both processes.)
Unlike the Java language, XML structures data into text. However, XML does not describe the type of data so contained. Such constraints for the data are described by XML Schema and encoding specifications, which are widely adopted in the world of Web services.
As the majority of Java objects and XML Schema constructs are supported by default, there is usually no need to customize serialization. The WSO2 SOA Enablement Server serialization framework does support customization, however. Both Default Serialization and Custom Serialization are covered in the following chapters.
WSO2 SOA Enablement Server for Java is able to handle quite complex XML Schemas used across the enterprise. Since the majority of Java objects and XML Schema constructs are supported, there is usually no need to customize serialization. This chapter focuses on XML Schema, Encoding types and Java Objects, which are serialized by WSO2 SOA Enablement Server for Java default serializers.
WSO2 SOA Enablement Server for Java supports the entire XSD Datatypes specification. Note that some XML Schema types can be deserialized to more then one Java type. In these cases, the Java type is chosen automatically (the deserializers recognize the target Java type). Table 9, “XML Schema to Java Mapping” and Table 10, “Java to XML Schema Mapping” list the mappings between Java types and XML Schema types.
Table 9. XML Schema to Java Mapping
Schema Type | Java Type |
---|---|
anySimpleType | java.lang.Object |
anyType | java.lang.Object |
anyURI | java.lang.String |
base64Binary | byte[] |
boolean | boolean |
boolean | java.lang.Boolean |
byte | byte |
byte | java.lang.Byte |
char | java.lang.String |
date | org.idoox.wasp.serialization.xsdbuiltin.Date |
dateTime | java.util.Date |
dateTime | java.sql.Date |
dateTime | java.sql.Time |
dateTime | java.sql.Timestamp |
dateTime | org.idoox.wasp.serialization.xsdbuiltin.DateTime |
decimal | java.math.BigDecimal |
double | double |
double | java.lang.Double |
duration | org.idoox.wasp.serialization.xsdbuiltin.Duration |
ENTITIES | java.lang.String |
ENTITY | java.lang.String |
float | float |
float | java.lang.Float |
gDay | org.idoox.wasp.serialization.xsdbuiltin.GDay |
gMonth | org.idoox.wasp.serialization.xsdbuiltin.GMonth |
gMonthDay | org.idoox.wasp.serialization.xsdbuiltin.GMonthDay |
gYear | org.idoox.wasp.serialization.xsdbuiltin.GYear |
gYearMonth | org.idoox.wasp.serialization.xsdbuiltin.GYearMonth |
hexBinary | org.idoox.wasp.serialization.xsdbuiltin.HexBinary |
ID | java.lang.String |
IDREF | java.lang.String |
IDREFS | java.lang.String |
int | int |
int | java.lang.Integer |
integer | java.math.BigInteger |
language | java.lang.String |
long | long |
long | java.lang.Long |
Name | java.lang.String |
NCName | java.lang.String |
negativeInteger | java.math.BigInteger |
NMTOKEN | java.lang.String |
NMTOKENS | java.lang.String |
nonNegativeInteger | java.math.BigInteger |
nonPositiveInteger | java.math.BigInteger |
normalizedString | org.idoox.wasp.serialization.xsdbuiltin.NormalizedString |
NOTATION | java.lang.String |
positiveInteger | java.math.BigInteger |
QName | javax.xml.namespace.QName |
short | short |
short | java.lang.Short |
string | java.lang.String |
string | char |
string | java.lang.Character |
time | org.idoox.wasp.serialization.xsdbuiltin.Time |
token | org.idoox.wasp.serialization.xsdbuiltin.Token |
unsignedByte | java.lang.Short |
unsignedInt | java.lang.Long |
unsignedLong | java.math.BigInteger |
unsignedShort | java.lang.Integer |
Table 10, “Java to XML Schema Mapping” contains the same information as Table 9, “XML Schema to Java Mapping” but sorted by Java type. Note that some Java types are serialized to more that one XML schema type. The XML schema type is then chosen automatically. Serializers can recognize the XML Schema used for serialization.
Table 10. Java to XML Schema Mapping
Java Type | Schema Type |
---|---|
boolean | boolean |
byte | byte |
byte[] | binary |
char | string |
double | double |
float | float |
int | int |
java.lang.Boolean | boolean |
java.lang.Byte | byte |
java.lang.Character | string |
java.lang.Double | double |
java.lang.Float | float |
java.lang.Integer | int |
java.lang.Long | long |
java.lang.Object | anyType |
java.lang.Object | anySimpleType |
java.lang.Object | simpleType |
java.lang.Object | urType |
java.lang.Short | short |
java.lang.String | string |
java.lang.String | timeInstant |
java.lang.String | uri |
java.lang.String | language |
java.lang.String | NMTOKEN |
java.lang.String | NMTOKENS |
java.lang.String | Name |
java.lang.String | NCName |
java.lang.String | ID |
java.lang.String | IDREF |
java.lang.String | IDREFS |
java.lang.String | ENTITY |
java.lang.String | ENTITIES |
java.lang.String | NOTATION |
java.math.BigDecimal | decimal |
java.math.BigInteger | integer |
java.math.BigInteger | non-negative-integer |
java.math.BigInteger | positive-integer |
java.math.BigInteger | non-positive-integer |
java.math.BigInteger | negative-integer |
javax.xml.namespace.QName | QName |
long | long |
java.sql.Date | dateTime |
java.sql.Time | dateTime |
java.sql.Timestamp | dateTime |
java.util.Date | dateTime |
org.idoox.wasp.serialization.xsdbuiltin.Date | date |
org.idoox.wasp.serialization.xsdbuiltin.Duration | timeDuration |
org.idoox.wasp.serialization.xsdbuiltin.Time | time |
short | short |
The WSO2 SOA Enablement Server for Java serialization framework maintains two serialization types, literal and encoded.
Literal serialization is used when the data being (de)serialized strictly follow the XML schema given in the WSDL document. This kind of serialization is used mostly for document/literal operations in the WSDL document.
Encoded serialization is used when the schema placed in the WSDL document describes only the logical (not the physical) structure of the data. An example of where the data can be referenced might be found in the SOAP specification, but the schema contains no such construct. In this case, the encoding layer is used as data source for (de)serializers.
Since document/literal is preferred by the majority of SOAP stacks, the following sections will concentrate on this kind of serialization.
In encoded mode, Java arrays are serialized into SOAP Encoding arrays. Example 137 gives the schema for such an array:
Example 137. XML Schema for Encoded Array
<xsd:complexType name="ArrayOfstring"> <xsd:complexContent> <xsd:restriction base="SOAP-ENC:Array"> <xsd:sequence maxOccurs="unbounded" minOccurs="0"> <xsd:element name="item" type="xsd:string"/> </xsd:sequence> <xsd:attribute ref="SOAP-ENC:arrayType" wsdl:arrayType="xsd:string[]"/> </xsd:restriction> </xsd:complexContent> </xsd:complexType>
For better compatibility with other SOAP toolkits, WSO2 SOA Enablement Server is able to process logically invalid schemas, such as schemas that do not define the element item.
In literal mode, arrays are generated to schema as elements with maxOccurs=unbounded attributes. Example 138 shows this.
In WSO2 SOA Enablement Server, XML Schema complexType members are mapped into either:
Public fields of Java classes, or
JavaBeans.
Mapping to public fields of Java classes is the default behavior, as in Example 139.
When a complex type is mapped to a JavaBean, a get/set pair of methods is generated for each member. Please see Example 140.
WSO2 SOA Enablement Server for Java 6.5.4 supports the following advanced XML schema constructs:
List datatype
Union datatype
Enumeration facet
The choice group element
The any element
XML Schema can represent a simple datatype (such as integer values) as a string containing whitespace-separated items. This datatype is called a list.
When such a datatype is processed by WSDL2Java, it is mapped into an array of the corresponding simple types, as shown in Example 141, Example 142 and Example 143.
Example 141. List Type: XML Schema
<xsd:element name="content" type="listType"/> <xsd:simpleType name="listType"> <xsd:list itemType="xsd:int"/> </xsd:simpleType>
XML Instance:
Java:
A simple datatype in XML Schema can hold more than one semantic type of information. Imagine you want a type to contain two different strings, 2002-12-05 and 320. The first one is a date, the second one is an integer. This type is thus a union of two other simple types.
Note that XML contains no information about the actual type contained. It is up to the user to handle and determine the type of data being represented. This is why union types are mapped to Java String types. The user gets the string and decides whether the information is in int or date form.
This is shown in Example 144, Example 145 and Example 146.
The enumeration facet limits a simple type to a set of distinct values. Any type you want can have this facet. (For example, a string defining color may be restricted to the texts red, blue and green.)
WSDL2Java generates an enumeration Java class for such types. If you want to create an enumeration class in Java and want Java2WSDL to create an enumeration-type XML Schema, follow these rules:
The enumerated class MUST directly extend the class java.lang.Object.
The class MUST NOT implement any interface.
The class MUST have only private constructors.
The class MUST have one or more attributes which are public static final.
This is shown in Example 147, Example 148 and Example 149.
Example 147. XML Schema
<xsd:element name="content" type="color"/> <xsd:simpleType name="color"> <xsd:restriction base="xsd:NMTOKEN"> <xsd:enumeration value="green"/> <xsd:enumeration value="blue"/> <xsd:enumeration value="red"/> </xsd:restriction> </xsd:simpleType>
Example 149. Java:
package example.processing.serialization; public class Color { // red and DISC_RED are equal to the enumerated value "red" in XML public static final Color red = new Color("red", 0); public static final int DISC_RED = 0; // blue and DISC_BLUE are equal to the enumerated value "blue" in XML public static final Color blue = new Color("blue", 1); public static final int DISC_BLUE = 1; // green and DISC_GREEN are equal to the enumerated value "green" in XML public static final Color green = new Color("green", 2); public static final int DISC_GREEN = 2; private String value; private int discriminator; /** * All constructors have to be private in an enumeration */ private Color( String value, int discriminator) { this.value = value; this.discriminator = discriminator; } public String getValue() { return value; } public int getDiscriminator() { return discriminator; } }
The choice group element allows only one of its children to appear in an instance.
Imagine you wish to create a countryCode element, which can represent either the phonecode of a country or the country name.
<countryCode> <country>CZ</country> </countryCode> or <countryCode> <phonecode>420</phonecode> </countryCode>
WSO2 SOA Enablement Server handles chooseable types in a special manner by a class which describes the type being held. To create your own chooseable class in Java, follow these rules:
A chooseable class MUST have a public constructor without any parameters.
A chooseable class MUST have a discriminator mechanism, an int getDiscriminator() method.
A chooseable class MUST have one or more choices:
Each choice consists of a couple:
public static final int DISC_{name} =....;
public {type, } get{name}();
public {type} set{name}();
Note that the setters must be implemented properly - that means that the set method is required to set the discriminator value. See Example 152 for a correct choice example.
![]() | Note |
---|---|
Corresponding names are recognized as case insensitive. |
Example 150. Choice: XML Schema
<xsd:element name="countryCode" type="countryCodeType"/> <xsd:complexType name="countryCodeType"> <xsd:choice> <xsd:element name="phonecode" type="xsd:int"/> <xsd:element name="country" type="xsd:string"/> </xsd:choice> </xsd:complexType>
Example 151. Choice: XML Instance Examples
<countryCode> <phonecode>420</phonecode> </countryCode> <countryCode> <country>CZ</country> </countryCode>
Example 152. Choice: Java
public class CountryCodeType { public static final int DISC_PHONECODE = 0; public static final int DISC_COUNTRY = 1; private java.lang.Integer phonecode; private java.lang.String country; private int disc; /** * */ public CountryCodeType() { super(); } /** * Choice constructor. */ public CountryCodeType( int disc, Object val) { super(); this.disc = disc; if (disc == DISC_PHONECODE) { phonecode = (java.lang.Integer) val; } if (disc == DISC_COUNTRY) { country = (java.lang.String) val; } } /** * Getter for phonecode. */ public java.lang.Integer getPhonecode() { return this.phonecode; } /** * Setter for phonecode. */ public void setPhonecode(java.lang.Integer phonecode) { this.disc = DISC_PHONECODE; this.phonecode = phonecode; } /** * Getter for country. */ public java.lang.String getCountry() { return this.country; } /** * Setter for country. */ public void setCountry(java.lang.String country) { this.disc = DISC_COUNTRY; this.country = country; } /** * Choice discriminator. */ public int getDiscriminator() { return disc; } }
WSO2 SOA Enablement Server is also able to handle complex schemas where choices are nested within other choices (xsd:choice containers) or sequences (xsd:sequence containers) In this case it is not obvious how to name the class generated to represent the nested choice. In this case WSO2 SOA Enablement Server generates a name Choice and for each additional nesting within a sequence or choice it appends Choice/Sequence to that name. When there are more such choices in one sequence, a number starting at 1 is appended.
An any structure specifies that any element of any type and any name can be included in a type's content model. WSO2 SOA Enablement Server represents this construct in Java as a DOM Element object.
This is shown in Example 153, Example 154 and Example 155.
Example 153. <any> Element: XML Schema
<xsd:element name="anyThingInside" type="anyThingInsideType"/> ... <xsd:complexType name="anyThingInsideType"> <xsd:sequence> <xsd:any/> </xsd:sequence> </xsd:complexType> ... </xsd:element>
WSO2 SOA Enablement Server offers wide support for all the basic types of Java collections. WSO2 SOA Enablement Server is able to interoperate with Microsoft.NET over all of these types of collections..NET collections are described in Java Collections and their Mapping to .NET.
![]() | Note |
---|---|
The user must choose the type of containers when installing WSO2 SOA Enablement Server. See Installation Options. |
Implementations of collections implementing any of the interfaces in Table 11, “Support for Java Interfaces in Collections” are supported by WSO2 SOA Enablement Server. This table also lists the Java classes representing the interfaces following deserialization by WSO2 SOA Enablement Server for Java.
The collections can be extended with public fields or JavaBeans. These are handled as described in Complex Types and Structures.
Iterators are an important part of Java collections. The following Java iterators are supported by WSO2 SOA Enablement Server:
Table 12. Support for Java Iterators in Collections
Java Iterator | Supported by: |
---|---|
List, Set and Map. | |
Older classes like Vector. |
The following Java implementations of collections and containers are supported by WSO2 SOA Enablement Server:
![]() | Note |
---|---|
XML Schema Representation of Java Collections There are pre-generated XML Schemas that include all the containers mentioned above. |
.NET-compatible collections are prepared for both RPC/encoded and document/literal style. These types should work with both MS.NET and Apache AXIS. There are some details which you should know.
List and set collections are represented as simple arrays in XML Schema. Schema structures must be strictly kept. For example, it is prohibited to send a complexType List via SOAP and set the array inside to null value.
Maps are represented as two arrays, named Keys and Values. They must conform to the following rules:
Neither of the arrays can be set to null.
Both arrays must be the same length.
An element in the Keys array at the nth position represents a key for the Values array at the nth position.
XML Schema descriptions of MS.NET compatible for SOAP encoded and SOAP literal are in .NET-Compatible Containers for SOAP Encoded and .NET-Compatible Containers for SOAP Literal.
You have to be careful when using collections with SOAP stacks from different vendors (such as.NET or Apache AXIS). There is no guarantee that the features of a particular collection will be preserved when sent as a SOAP message.
If SOAP stacks from different vendors do not understand how each other represents a collection, that collection will be serialized as a simple array.
The server side works with a class representing a collection and all its effort, but in a SOAP message this type is serialized just as an array.
You need to ask the following questions when sending collections between different vendors' SOAP stacks:
What is the interoperability between the representations of collections in the different SOAP stacks?
Will the order be kept? For example, will members of a collection stay in the same order?
Will duplicate members be collapsed into a single member?
The answer in general is: Nothing can be predicted with certainty. Serialization takes each item of a type (collection or array) and puts it into a SOAP message as-is, in the order it gets while iterating over the items. What happens inside the collection and how items will be reordered depends completely on the implementation.
There are several official versions of XML Schema:
version 2001 (namespace is http://www.w3.org/2001/XMLSchema)
version 2000/10 (namespace is http://www.w3.org/2000/10/XMLSchema)
version 1999 (namespace is http://www.w3.org/1999/XMLSchema)
WSO2 SOA Enablement Server almost fully supports 2001 XML Schema. This includes support for all the simple datatypes and most of the definition constructs. The previous versions of XML Schema are partially supported, for backward compatibility. This includes support for nearly all of the simple datatypes.
All java exceptions thrown during service handling are (de)serialized as faults. Unlike previous versions, WSO2 SOA Enablement Server now transports not only the exception stack trace but, under certain conditions, the exception fields as well. The implementation is JAX-RPC compliant. For further information see the Java API for XML-Based RPC (JAX-RPC) Documentation.
Certain conditions must be met for structured SOAP faults to be (de)serialized. These conditions are in two groups:
Restrictions applied to the exception fields:
Only fields with a public getter method can be (de)serialized. A field which has a non-public getter method or no getter method at all, is not (de)serialized.
The exception field must obey the rules for standard serialization.
None of the fields may extend the java.lang.Throwable class (directly or indirectly).
Restrictions applied to the exception itself:
The exception cannot extend java.lang.RuntimeException or any of its subclasses. It can extend any of the other subclasses of java.lang.Exception and must extend one of them or Exception itself.
The exception full name must not start with java., javax. or com.sun. prefixes (for example, java.lang.IllegalAccessException, java.security.SignatureException, etc.).
If the exception meets the restrictions above, those of its fields with public getter methods are (de)serialized. Otherwise, only the exception stack trace is transported.
Let's look at a simple example. Consider the following exception:
Example 156. Sample Java Exception
public class InvalidIndexException extends Exception { private int currentIndex; private String[] history; private LastItem previousItem; public InvalidIndexException(int currentIndex, String[] history, LastItem previousItem) { super(); this.currentIndex = currentIndex; this.history = history; this.previousItem = previousItem; } public InvalidIndexException(String msg) { super(msg); } public int getCurrentIndex() { return currentIndex; } public String[] getHistory() { return history; } public LastItem getPreviousItem() { return previousItem; } }
The snippet of a WSDL file describing the exception above and the operation where the exception may be thrown will look like this:
Example 157. WSDL Describing Exception
... <xsd:complexType name="InvalidIndexException"> <xsd:annotation> <xsd:appinfo> <map:java-type name="demo.basic.exceptions.server.InvalidIndexException"/> </xsd:appinfo> </xsd:annotation> <xsd:sequence> <xsd:element name="message" nillable="true" type="xsd:string"/> <xsd:element name="currentIndex" type="xsd:int"/> <xsd:element name="history" nillable="true" type="xns5:ArrayOfstring"/> <xsd:element name="previousItem" nillable="true" type="tns:LastItem"/> </xsd:sequence> </xsd:complexType> ... <wsdl:message name="InvalidIndexException"> <wsdl:part element="ns0:InvalidIndexException" name="InvalidIndexException"/> </wsdl:message> ... <wsdl:portType name="ExceptionsService"> <wsdl:operation name="getItem" parameterOrder="p0"> <wsdl:input message="tns:ExceptionsService_getItem__Request_Soap"/> <wsdl:output message="tns:ExceptionsService_getItem_Response_Soap"/> <wsdl:fault message="tns:InvalidIndexException" name="InvalidIndexException"/> </wsdl:operation> </wsdl:portType> ...
The SOAP message that is sent when the exception is thrown will look like:
Example 158. Structured SOAP Fault dump
<?xml version="1.0" encoding="UTF-8"?> <e:Envelope xmlns:d="http://www.w3.org/2001/XMLSchema" xmlns:e="http://schemas.xmlsoap.org/soap/envelope/" xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns:wn0="http://wso2.com/xsd/SchemaTypes/" xmlns:wn1="http://wso2.com/wsdl/demo/basic/exceptions/server/" xmlns:wn2="http://wso2.com/wsdl/java/lang/"> <e:Body> <e:Fault xmlns:ns0="http://schemas.xmlsoap.org/wsdl/soap/"> <faultcode xmlns="">ns0:Server</faultcode> <faultstring xmlns="">class demo.basic.exceptions.server.InvalidIndexException:</faultstring> <detail xmlns=""> <wn0:InvalidIndexException i:type="wn1:InvalidIndexException"> <wn1:message i:nil="true"/> <wn1:currentIndex i:type="d:int">101</wn1:currentIndex> <wn1:history i:type="wn2:ArrayOfstring"> <wn2:string i:type="d:string">Item 34</wn2:string> <wn2:string i:type="d:string">Item 55</wn2:string> <wn2:string i:type="d:string">Item 34</wn2:string> <wn2:string i:type="d:string">Item 55</wn2:string> <wn2:string i:type="d:string">Item 34</wn2:string> <wn2:string i:type="d:string">Item 55</wn2:string> <wn2:string i:type="d:string">Item 34</wn2:string> <wn2:string i:type="d:string">Item 55</wn2:string> <wn2:string i:type="d:string">Item 34</wn2:string> <wn2:string i:type="d:string">Item 55</wn2:string> </wn1:history> <wn1:previousItem i:type="wn1:LastItem"> <wn1:index i:type="d:int">55</wn1:index> <wn1:value i:type="d:string">Item 55</wn1:value> </wn1:previousItem> </wn0:InvalidIndexException> </detail> </e:Fault> </e:Body> </e:Envelope>
WSO2 SOA Enablement Server for Java supports two options in mixed content handling. The first one (as default; the only supported by the WSO2 SOA Enablement Server for Java 6.5.1 including and previous one) is to ignore the mixed attribute and map nested subelements to java types as usual.
The other option is to map the whole XML content (including all attributes) to an instance of org.w3c.dom.Element. The name of the element (returned from deserialization) is not a part of the value because it is not determined by the type but by its usage in the schema, and therefore it should be ignored. This implies that the WSO2 SOA Enablement Server for Java serializations, which while creating the output message, replace the provided element name with the name determined by the schema.
The decision whether to map some mixed content complex type to DOM must be done at development time. Wsdl2java (and schema2java) tools support the --mixed-to-dom option which sets this handling for all mixed content types. Otherwise, it is possible to use the --map-type option to map only selected types.
XML Schema anyType is mapped to java.lang.Object. Usually, the XML instance (the SOAP message) contains the xsi:type attribute which allows to determine the polymorphic type and use more specific java type as a deserialization target. During the serialization, the WSO2 SOA Enablement Server for Java provides xsi:type filled with the XML Schema corresponding to the runtime java type of the serialized object.
However, if the message contains no xsi:type attribute on an element of type anyType, it is not possible to choose some java type and so the WSO2 SOA Enablement Server for Java maps the element's content to org.w3c.dom.Element. Serialization of DOM Element as a value is also supported. In fact, this is the same as the processing of mixed content mapped to DOM as described in the previous section.
The WSO2 SOA Enablement Server serialization framework is extensible. The design of this framework allows you to customize the serialization process. The framework associates a serialization chain to every WSO2 SOA Enablement Server Web service endpoint and for every WSO2 SOA Enablement Server Web service client (created proxy). The serialization chain itself gives priorities to all serializers and deserializers that are used during Java to XML (or XML to Java) conversions. You are free to modify this chain and thus extend serialization to meet your requirements.
You can customize Serialization in the following ways:
No changes
Sufficient for cases when the serialized classes follow the JavaBeans pattern (containing public fields or public get/set methods, public default constructor). See Default Serialization.
Writing BeanInfo class
Creates a JavaBeans-compatible class from a non-JavaBean class. See Appendix: Customizing JavaBeans Serialization.
Activating serialization of private fields
Ignores get/set methods. All fields that are not transient or final are serialized. See Appendix: Private Fields. Be aware of possible security consequences.
Creating and configuring custom serializer and deserializer classes
Use when none of the above cases apply. The remainder of the chapter will focus on this case.
Before writing your (de)serializers, you need to decide whether you want to communicate using literal or encoded mode. The following sections contain examples for the literal mode of communication. For an example of an encoding-based serializer, please see the MessageFormat Serialization demo.
The (de)serialization in WSO2 SOA Enablement Server is stream-based. This means that data are deserialized when they arrive; no tree structure is built from them.
The following sections refer to the Book class and known XML Schema for this class, detailed below. You have to write the (de)serializer, because the Book class has no default public constructor. If it did, (de)serialization based on JavaBeans would work. See the src/example/processing/serialization examples and the demo directories of WSO2 SOA Enablement Server distribution for more serialization examples.
Example 159. Book Class - Java Source Code
package example.processing.serialization.library; /** * Represents a book item in a simple * library system. */ public class Book { /** book name */ public String name; /** book author */ public String author; /** * Constructor. * @param name book name * @param author book author * @throws IllegalArgumentException name or author is null */ public Book( String name, String author) { if (name == null) { throw new IllegalArgumentException("Name is null!"); } if (author == null) { throw new IllegalArgumentException("Author is null!"); } this.name = name; this.author = author; } /** * Test for equality. * @param object any object * @return true if books are equal */ public boolean equals(Object object) { if (!(object instanceof Book)) { return false; } Book secondBook = (Book) object; return name.equals(secondBook.name) && author.equals(secondBook.author); } }
Example 160. Book XML Schema
<?xml version="1.0" encoding="UTF-8"?> <xsd:schema targetNamespace="http://wso2.com/wsdl/example/processing/serialization/library/book" xmlns:tns="http://wso2.com/wsdl/example/processing/serialization/library/book" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <!-- Example of instance: <book> <name>Robinson Crusoe</name> <author>Daniel Defoe</author> </book> --> <xsd:complexType name="Book"> <xsd:sequence> <xsd:element name="name" type="xsd:string"/> <xsd:element name="author" type="xsd:string"/> </xsd:sequence> </xsd:complexType> </xsd:schema>
Steps needed for custom serialization to work:
Implementing Serializer
Implementing Deserializer
Configuring Custom Serialization
Serializers convert Java objects into XML instances. The serializer for Book instances is shown in Example 161. An explanation of its action follows.
Example 161. Book Service Serializer
package example.processing.serialization.library.serializer; // Helps with common serialization problems import com.idoox.wasp.serialization.SerializationHelper; // This class is serialized import example.processing.serialization.library.Book; import org.idoox.config.Configurable; import org.idoox.wasp.serialization.SerializationChain; import org.idoox.wasp.serialization.SerializationContext; import org.idoox.wasp.serialization.XMLSerializer; import org.idoox.xml.TokenWriter; import org.idoox.xmlrpc.MessageCreatingException; import javax.xml.namespace.QName; /** * Serializes Book instance. For example: instance * new Book("Robinson Crusoe","Daniel Defoe"). * is serialized to * * <book> * <name>Robinson Crusoe</name> * <author>Daniel Defoe</author> * </book> * */ public class BookSerializer implements XMLSerializer { //Default constructor will be generated for this class by Java compiler public void init(Configurable configurable) { // nothing to do, this serializer has no configuration } public String getMechanismType() { // this always has to be MECHANISM_TYPE, // needed for serialization framework return MECHANISM_TYPE; } /** * Serialization method. * @param tokenWriter: the writer to which the object is serialized * @param o: the data being serialized * @param serializationContext: a schema context, drives the * serialization process * @param serializationChain: a serialization chain, holds other * possible serializers * @throws MessageCreatingException if something goes wrong */ public void serialize( TokenWriter tokenWriter, Object o, SerializationContext serializationContext, SerializationChain serializationChain) throws MessageCreatingException { try { // we know that 'o' is non-null and it is a 'Book' type, // because we configure this serializer based upon the // BasicConstruct serializer, which handles null values // properly Book book = (Book) o; // get information about construct in XML QName schemaTypeName = serializationContext.getCurrentConstructSchemaType() .getName(); QName memberName = serializationContext.getCurrentConstructName(); // write start of a 'Book' structure tokenWriter.enter( memberName.getNamespaceURI(), memberName.getLocalPart()); SerializationHelper.writeXsiType(tokenWriter, schemaTypeName); // enter a 'Book' construct in XML Schema SerializationContext.State state; state = serializationContext.enterCurrentMember(); serializationContext.nextSchemaConstruct(); // serialize member 'String name' SerializationHelper.serialize(tokenWriter, book.name, serializationContext); // serialize member 'String author' SerializationHelper.serialize(tokenWriter, book.author, serializationContext); // leave 'Book' construct in XML Schema serializationContext.leave(state); // write end of 'Book' structure tokenWriter.leave(); } catch (MessageCreatingException e) { e; } catch (Exception e) { throw new MessageCreatingException("Failed while serializing Book", e); } } public void grabReferences( Object o, SerializationContext serializationContext, SerializationChain serializationChain) throws MessageCreatingException { // in literal (XMLSerializer interface) this does not matter, // this method is intended for encoded serialization } }
public class BookSerializer implements XMLSerializer
WSO2 SOA Enablement Server serializers have to implement the XMLSerializer interface.
Default constructor
The default constructor is required by the WSO2 SOA Enablement Server serialization framework.
public void init
Serializers can be optionally initialized using custom configurables. Such customization must be written into the deployment descriptor.
public String getMechanismType()
WSO2 SOA Enablement Server Serializers should always return MECHANISM_TYPE constants. This method allows you to reuse your serializers in the future.
public void serialize(...)
This method is invoked by the serialization framework to let the data be serialized. Note that the code inside must allow access from more than one thread.
Book book = (Book) o;
Serializer knows about the type that it is intended for. Overcasting is necessary to access data.
Get information about construct in XML
Serializers are driven by XML Schema declaration, all following operations are performed using information about declared structure.
Write start of a 'Book' structure
Start element of XML structure is written to the stream. XML Instance type is also written to the stream. This information then helps deserialize (useful for type polymorphism, such as serialization of java.util.Vector containing Book instances).
Enter a 'Book' construct in XML Schema
XML Schema context is changed to match content of XML structure. We have to do so because of WSO2 SOA Enablement Server's XML Schema-driven approach.
Serialization of XML schema construct members
Let the WSO2 SOA Enablement Server serialization framework serialize members. You can reuse the serializers so created.
Leave 'Book' construct in XML Schema
As we have previously entered the Book construct, we have to leave it.
Write end of 'Book' structure
As we have previously started the Book structure, we must end it.
catch (MessageCreatingException e)
We have to catch this exception because we do not want to wrap MessageCreatingException again.
catch (Exception e)
This only rethrows the exception wrapped in MessageCreatingException. It could be, for example, IOException or SchemaException.
public void grabReferences
Used only for encoded serialization.
Deserializers convert XML instances into Java objects. The deserializer for Book instances is shown below. An explanation of serializer source code follows.
Example 162. Deserializer for Book Instances
package example.processing.serialization.library.deserializer; import org.idoox.wasp.serialization.XMLDeserializer; import org.idoox.wasp.serialization.DeserializationContext; import org.idoox.wasp.serialization.DeserializationChain; import org.idoox.xml.Tokenizer; import org.idoox.xml.Token; import org.idoox.xmlrpc.MessageProcessingException; import org.idoox.config.Configurable; // Book objects are deserialized import example.processing.serialization.library.Book; // Helps with common serialization problems import com.idoox.wasp.serialization.SerializationHelper; /** * Deserializes XML structure to Book instance. * For example: input * * <book> * <name>Robinson Crusoe</name> * <author>Daniel Defoe</author> * </book> * * id deserialized class ten equals to * new Book("Robinson Crusoe","Daniel Defoe"). */ public class BookDeserializer implements XMLDeserializer { //Default constructor will be generated for this class by Java compiler public void init(Configurable configurable) { // nothing to do, this deserializer has no configuration } public String getMechanismType() { // this have to be always MECHANISM_TYPE, // needed for serialization framework return MECHANISM_TYPE; } public Object deserialize(Tokenizer tokenizer, Token token, Class targetClass, DeserializationContext deserializationContext, DeserializationChain deserializationChain) throws MessageProcessingException { try { // skip start of a 'Book' structure SerializationHelper.nextNonWhitespace(tokenizer); // enter a 'Book' construct in XML Schema DeserializationContext.State state; state = deserializationContext.enterCurrentMember(); deserializationContext.nextSchemaConstruct(); // deserialize member 'String name' String name = (String) SerializationHelper.deserialize(tokenizer, token, String.class, deserializationContext); // go to next XML element SerializationHelper.nextNonWhitespace(tokenizer); // deserialize member 'String author' String author = (String) SerializationHelper.deserialize(tokenizer, token, String.class, deserializationContext); // go to next XML element SerializationHelper.nextNonWhitespace(tokenizer); // leave 'Book' construct in XML Schema deserializationContext.leave(state); // return deserialized object return new Book(name, author); } catch (MessageCreatingException e) { throw e; } catch (Exception e) { throw new MessageProcessingException("Failed while deserializing Book", e); } } }
public class BookDeserializer implements XMLDeserializer
WSO2 SOA Enablement Server deserializers must implement org.idoox.wasp.serialization's XMLDeserializer interface.
Default constructor
The default constructor is required by the WSO2 SOA Enablement Server serialization framework.
public void init
Deserializers can be optionally initialized using custom configurables. Such customization has to be written into the deployment descriptor.
public String getMechanismType()
WSO2 SOA Enablement Server Deserializers should always return MECHANISM_TYPE constants. This method allows you to reuse your deserializers in the future.
public Object deserialize(...)
This method is invoked by the serialization framework to let the data be deserialized. Note that the code inside must allow multiple access from more than one thread.
Skip start of a 'Book' structure
Start XML tag is ignored, because it is used for deserialization.
Enter a 'Book' construct in XML Schema
Deserializers are driven by the XML Schema declaration. All subsequent operations are performed using information about declared structure.
Deserialization of XML schema construct members
We just let the WSO2 SOA Enablement Server serialization framework deserialize members. We can reuse the deserializers so created. After deserialization of a member, the stream is ready to read another member (the nextNonWhitespace method is used).
Leave 'Book' construct in XML Schema
As we have previously entered the Book construct, we must leave it.
Return deserialized object
Return constructed value.
catch (MessageCreatingException e)
We have to catch it because we do not want to wrap MessageCreatingException again.
catch (Exception e)
This only rethrows the exception wrapped in MessageCreatingException. This could be, for example, IOException or SchemaException.
If you have implemented a serializer and a deserializer, you also have to configure them. The configuration of a serializer is written as a regular deployment descriptor. The data inside it tells WSO2 SOA Enablement Server how to use your custom serializers. Example 163 shows a deployment descriptor for using the serializer and deserializer that were created in the previous two sections. The Serializer, Deserializer, Java class, XML Schema and deployment descriptor will together create a WSO2 SOA Enablement Server package that can be referenced as a serialization component.
Example 163. Deployment Descriptor for Configuration of Book Serialization
<?xml version="1.0" encoding="UTF-8"?> <package client-package="true" name="BookSerializers" targetNamespace="http://wso2.com/example/library/book" version="1.0" xmlns="http://wso2.com/wasp/package/1.1" xmlns:book= "http://wso2.com/wsdl/example/processing/serialization/library/book" xmlns:tns="http://wso2.com/example/library/book" xmlns:wasp="http://wso2.com/wasp/app/builtin_serialization"> <dependency ref="wasp:builtin_serialization" version="1.0"/> <processing name="BookProcessing"> <use ref="wasp:XmlSerialization"/> <schema location="Book.xsd" namespaceURI= "http://wso2.com/wsdl/example/processing/serialization/library/book"/> <type-mapping name="BookMapping"> <type maps-to="example.processing.serialization.library.Book" name="book:Book"/> </type-mapping> <serialization deserializer-class= "example.processing.serialization.library.deserializer.BookDeserializer" following-parts="wasp:XMLReflectionSerializer" name="BookSerizalization" preceding-parts="wasp:XMLBasicConstructSerializer" serialized-type="book:Book" serializer-class= "example.processing.serialization.library.serializer.BookSerializer"/> </processing> <export push="true"> <use ref="tns:BookProcessing"/> </export> </package>
package name="BookSerializers" ...
The name BookSerializers in the namespace http://systinet.com/wsdl/example/processing/serialization/library/ fully qualifies the package for (de)serialization of Book. Other namespaces are also defined here, because they are reused in the following elements of the deployment descriptor. We also define that this packages is of client type. Customized serialization can then be reused on both server and client side.
<dependency ref="wasp:builtin_serialization" version="1.0"/>
Dependency on WSO2 SOA Enablement Server built-in serialization is used here because we will extend WSO2 SOA Enablement Server's default serialization chain (XmlSerialization) for literal serialization with our Book serialization.
processing name="BookProcessing"
We define the name of the processing. This processing can be referenced by service-endpoint and service-client elements of deployment descriptors to explicitly say that they require modified serialization. The processing itself is used here to construct the serialization chain.
<use ref="WSO2 SOA Enablement Server:XmlSerialization"/>
Serialization chain for BookProcessing is constructed using the wasp:XmlSerialization serialization chain, which is extended with serializers/deserializers for Book class.
<schema ... />
Definition of XML schema based on its namespace URI and location of XML Schema file. Note that the namespaceURI attribute matches the targetNamespace used in Book.xsd, which will have to be contained in the package.
type-mapping name="BookMapping"
We define type mapping between XML Schema and Java types. This information helps the WSO2 SOA Enablement Server serialization framework select a serializer/deserializer during the processing of messages. It also allows WSO2 SOA Enablement Server to optimize serialization.
serialization name="BookSerizalization"
We define the serialization part named BookSerizalization and place it into the WSO2 SOA Enablement Server:XmlSerialization serialization chain before WSO2 SOA Enablement Server:XMLReflectionSerializer and after WSO2 SOA Enablement Server:XMLBasicConstructSerializer. We also define that this part handles the book:Book XML Schema complex type, which is defined in Book.xsd. We insert this serialization part before reflection serializer because we do not want to use reflections to serialize/deserialize Book. This was one of the reasons why we wrote a custom serializer and deserializer for Book. Serializer and Deserializer classes are defined using serializer-class and deserializer-class attributes. For more details about default serialization chains see Appendix: Serialization Chains.
export push="true"
We define that the BookProcessing is pushed. If you include a package containing such a deployment descriptor into your CLASSPATH, then the BookSerialization is used as default. We do not have to define this processing as pushed, but if we do, we must then configure serialization using the processing attribute of each service-endpoint and service-client element of the deployment descriptors of packages that want to use our modified BookSerialization. Note that the classpath for applications deployed on the server side is constructed using dependency elements. Thus we say that we require a wasp:builtin_serialization in every CLASSPATH that also contains a package jar file with this deployment descriptor.
Back to the serialization example, now we have:
Java source code: the Book code.
XML Schema of the Book complex type.
Java source code for custom Serializer and Deserializer.
A deployment descriptor that puts all the above elements together and configures custom serialization.
We have to compile Java sources and create a WSO2 SOA Enablement Server package named book_serializers.jar. Because sources are in the src/example/processing/serialization/library of the WSO2 SOA Enablement Server installation, we are ready to do so. Instructions are below. Commands are written for Windows; if you are on a UNIX machine, change the terms appropriately.
Go to the WASP_HOME/src/example/processing/serialization/library directory. Create a subdirectories named classes, clientclasses and serviceclasses in this directory.
Set the WASP_HOME system variable to the absolute path of your WSO2 SOA Enablement Server for Java installation (for example: SET WASP_HOME=c:\systinet\ssj65).
Compile Java sources using
javac -classpath "%WASP_HOME%/lib/wasp.jar" --sourcepath "%WASP_HOME%/src" -d classes "%WASP_HOME%/src/example/processing/ serialization/library/serializer/BookSerializer.java" "%WASP_HOME%/src/example/processing/ serialization/library/deserializer/BookDeserializer.java"
Now that we have compiled all the source code, we create a WSO2 SOA Enablement Server package jar named book_serializers.jar. To do so, execute the command:
"%WASP_HOME%\bin\WaspPackager.bat" -C -o book_serializers.jar -s classes -a "%WASP_HOME%/src/example/processing/serialization/library/Book.xsd" --dd "%WASP_HOME%/src/example/processing/serialization/library/ serializers_package.xml" --force
Now we have the package book_serializers.jar with customizer serialization. On the client side, you only need to have it in your CLASSPATH. On the server side, book_serializers.jar must be deployed, as packages that want to use this serialization have to refer to this package using their deployment descriptors. Examples follow.
An example service is in src/example/processing/serialization/library/service/TestBookServiceImpl.java Ensure that you are in the previously created test directory, then create the service classes and compile the service using
javac -classpath "%WASP_HOME%/lib/wasp.jar;book_serializers.jar" -d serviceclasses "%WASP_HOME%/src/example/processing/serialization/library/service/TestBookServiceImpl.java"
Now we will create a WSDL for the TestBookServiceImpl class. Run:
"%WASP_HOME%\bin\Java2WSDL" --classpath serviceclasses;book_serializers.jar example.processing.serialization.library.service.TestBookServiceImpl --deployment-descriptor "%WASP_HOME%/src/example/processing/serialization/library/serializers_package.xml" --processing BookProcessing --insert "Book.xsd" --force
We used the deployment descriptor to tell Java2WSDL that there is a mapping of Book to XML Schema, that this mapping is defined in the BookProcessing and that the XML Schema for the Book complex type is to be inserted into the WSDL.
Next, we will create a deployment descriptor for TestBookService that will refer to BookSerializer. This deployment descriptor is below. We define a dependency on the BookSerializers package using the ser namespace that is equal to the target namespace of the deployment descriptor contained in book_serializers.jar. We also define two service endpoints for one service instance: the SecondTestBookService explicitly defines BookProcessing. It is shown only for demonstration. It is not required because BookProcessing is defined as pushed. Thus the configuration for both endpoints is the same.
Example 164. Deployment Descriptor for TestBookService (Refers to BookSerializer)
<?xml version="1.0" encoding="UTF-8"?> <package client-package="false" name="TestBookServicePackage" targetNamespace="http://wso2.com/example/processing/serialization/library/service" version="1.0" xmlns="http://wso2.com/wasp/package/1.1" xmlns:ser="http://wso2.com/wsdl/example/processing/serialization/library/" xmlns:tns="http://wso2.com/example/processing/serialization/library/service"> <dependency ref="ser:BookSerializers" version="1.0"/> <service-instance implementation-class="example.processing.serialization.library.service. TestBookServiceImpl" instantiation-method="shared" name="TestBookServiceImplInstance" preload="false"/> <service-endpoint name="TestBookService" path="/TestBookService" service-instance="tns:TestBookServiceImplInstance" type="java"> <wsdl service="wsdlns:TestBookServiceImpl" uri="Definitions_example_processing_serialization_library_service.wsdl" xmlns:wsdlns="http://wso2.com/wsdl/example/processing/serialization/library/service/"/> </service-endpoint> <service-endpoint name="SecondTestBookService" path="/TestBookService2" processing="ser:BookProcessing" service-instance="tns:TestBookServiceImplInstance" type="java"> <wsdl service="wsdlns:TestBookServiceImpl" uri="Definitions_example_processing_serialization_library_service.wsdl" xmlns:wsdlns="http://wso2.com/wsdl/example/processing/serialization/library/service/"/> </service-endpoint> </package>
We create a package for TestBookService using the command:
"%WASP_HOME%\bin\WaspPackager.bat" -p TestBookServicePackage -o testBookServicePackage.jar -s serviceclasses --dd "%WASP_HOME%/src/example/processing/serialization/library/service_package.xml" -w Definitions_example_processing_serialization_library_service.wsdl
Note that the Book class must not be contained in book_serializers.jar, to avoid classloader problems on the server side.
Now you have to deploy the book_serializers.jar and testBookServicePackage.jar files to the server.
Ensure that the server is running.
Deploy the book_serializers.jar using:
"%WASP_HOME%\bin\Deploy.bat" book_serializers.jar -t http://localhost:6060.
This package has to be deployed first, before testBookServicePackage.jar references its deployment descriptor.
Deploy the testBookServicePackage.jar package using:
Deploy testBookServicePackage.jar -t http://localhost:6060
Now you can view WSDLs at http://localhost:6060//TestBookService/wsdl and http://localhost:6060//TestBookService2/wsdl. Services are configured to use custom serialization.
On the client side, when the processing containing customized serialization is pushed, you only have to include the package you generated with customized serialization. When this processing is not pushed, you have to write a client side deployment descriptor with the service-client element, which will declare which serialization to use based on the WSDL port-type qualified name. Instruction for client compilation follows.
Create the clientclasses directory.
Run:
javac -sourcepath "%WASP_HOME%/src" -classpath "%WASP_HOME%/lib/wasp.jar;book_serializers.jar" -d clientclasses "%WASP_HOME%/src/example/processing/serialization/library/test/Test.java".
Run the Test client using:
java -classpath "%WASP_HOME%/lib/wasp.jar;book_serializers.jar;clientclasses" example.processing.serialization.library.test.Test.
Private fields can be serialized (usually done for integration). In this case, all fields that are not marked as transient, final or static are serialized. In this mode, get/set methods are ignored.
Serialization of private fields can be set up only for the whole server. To set it up, go to the %WASP_HOME%\conf directory (On UNIX: $WASP_HOME/conf), find the serialization module in clientconf.xml and serverconf.xml, and edit it as shown in Example 165.
Example 165. Serialization Module Edited to Serialize Private Fields
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE module PUBLIC <module name="SerializationRepository" loader="com.idoox.wasp.serialization.SerializationRepository" classSpace="root.wasp-impl" serializePrivateFields="true"> ... </module>
The JavaBeans pattern requires that all your structures (classes, which holds properties) have public fields or public get/set methods and a public default constructor. The following examples show three Java classes serialized by WSO2 SOA Enablement Server in the same way:
Example 166. JB1 Book Class - Public Fields
package com.mylibrary1; /** * Represents an item about book in a simple * library system. */ public class Book { /** book name */ public String name; /** * Default constructor. */ Book() { } }
Example 167. JB2 Book Class - Getters and Setters
package com.mylibrary2; /** * Represents a book item in a simple * library system. */ public class Book { /** book name */ private String name; // No Constructor written, default constructor // will be generated by Java compiler /** * Getter for property name. * @return book name */ public String getName() { return name; } /** * Setter for property name. * @param name book name */ public void setName(String name) { this.name = name; } }
Example 168. Book Class that Needs to be modified by BeanInfo
package com.mylibrary3; /** * Represents a book item in a simple * library system. */ public class Book { /** book name */ private String name; /** book author */ private String author; /** * Default constructor sets the * author property to "me". */ public Book() { this.author = "me"; } /** * Getter for property name. * @return book name */ public String getBookName() { return name; } /** * Setter for property name. * @param name book name */ public void setBookName(String name) { this.name = name; } /** * Getter for property author. * @return book author */ public String getAuthor() { return author; } /** * Setter for property author. * @param author book author */ public void setAuthor(String author) { this.author = author; } }
The last Book class contains the author property. The name property, known from previous Book implementations, is here called bookName. Because of this, we tell java.beans.Introspector that the Book class has only one property and its name is not bookName, but name.
Example 169. Book Class - BeanInfo Description
package com.mylibrary3; import java.beans.PropertyDescriptor; import java.beans.SimpleBeanInfo; import java.lang.reflect.Method; public class BookBeanInfo extends SimpleBeanInfo { publicPropertyDescriptor[] getPropertyDescriptors() { System.out.println("BookBeanInfo.getPropertyDescriptors() invoked"); try { Class[] paramTypes = new Class[0]; Method getMethod = Book.class.getMethod("getBookName", paramTypes); paramTypes = new Class[] { String.class }; Method setMethod = Book.class.getMethod("setBookName", paramTypes); PropertyDescriptor nameProperty = new PropertyDescriptor("name", Book.class); nameProperty.setReadMethod(getMethod); nameProperty.setWriteMethod(setMethod); PropertyDescriptor[] ret = new PropertyDescriptor[1]; ret[0] = nameProperty; return ret; } catch (Exception e) { System.err.println("Something wrong, should never be!"); e.printStackTrace(); throw new RuntimeException("Unexpected exception!"); } } }
Note that:
The BookBeanInfo class is in the same package as Book.
BookBeanInfo is derived from Book by appending BeanInfo.
BookBeanInfo describes that the Book class has the name property, which can be read by the getBookName() and setBookName() methods.
The author property follows the JavaBeans pattern, but the BookBeanInfo class excludes this property of Book.
Default Serialization chains are packed in the builtin_serialization.jar. For more details see its deployment descriptor. For XML Serialization, two serialization parts are necessary for your customization:
WSO2 SOA Enablement Server:XMLBasicConstructSerializer
Handles null values, references, array and XML Schema primitive types. This serialization part is typically configured to be at the beginning of an extended serialization chain.
WSO2 SOA Enablement Server:XMLReflectionSerializer
Uses Java Reflection API to (de)serialize Java/XML objects (based on JavaBeans pattern). This serialization part is typically configured to come after custom serializers.