Using User -Defined Data TypesSometimes your Web service might require the use of classes, in parameters or return values, that are user defined ”in other words, not built into the Java language or WebLogic Server. If your back-end component(s) that uses these user-defined types is already written, chances are your UDT Java classes are also already written. In most cases, the WebLogic ServiceGen task should be able to examine these UDTs and generate the following for you:
These are all necessary items in the make-up of a WebLogic Web service. The good news is that if your UDT conforms to the following rules, WebLogic Server should be able to successfully generate them for you:
Otherwise, you need to manually create these items. The following sections describe how this can be done. Specifying XML Schema for UDTsYou need to decide how your user-defined type will look in the XML representation. For instance, what element names and tag structure will be used? Which elements are required, and which ones are optional? How many times can a particular element occur? The XML representation you define will be used in the XML SOAP request and possibly in the SOAP response. You define how your UDT will "look" in XML using the precise language of XML Schemas. Learning how to author XML Schemas is not a trivial task and is certainly beyond the scope of this book. We can, however, show a sample schema definition for a typical UDT, Employee , as defined in Listing 30.4. (Note that the Employee class conforms to the rules presented in the preceding section.) Listing 30.4 Sample User-Defined Type Employeepublic class Employee { private String fullname; private int id; public Employee() {} // Create a default employee // set/get methods for each variable for JavaBean compliance public setFullname(String name) { fullname = name; } public getFullname() { return fullname ; public setId(int serialNo) { id = serialNo; } public getId() { return id; } } A possible XML representation for an instance of Employee might be <employee> <fullname>Christy Anne Go</fullname> <id>98345</id> </employee> Now you must codify the rules indicating how to convert an instance ”in fact, any instance ”of Employee into the preceding XML format. What you need is a meta language to describe a grammar for what are valid XML sequences that represent a correct Employee instance. That is exactly what an XML Schema is. A possible schema for Employee is shown in Listing 30.5. Listing 30.5 A Sample XML Schema for a User-Defined Type<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:stns="java:examples.webservices" attributeFormDefault="qualified" elementFormDefault="qualified" targetNamespace="java:examples. webservices "> <xsd:complexType name="Employee"> <xsd:sequence> <xsd:element name="fullname" type="xsd:string" maxOccurs="1" minOccurs="1"> </xsd:element> <xsd:element name="id" type="xsd:int" maxOccurs="1" minOccurs="1"> </xsd:element> </xsd:sequence> </xsd:complexType> </xsd:schema> This schema declares the following:
Note Go to http://www.w3.org/TR/xmlschema-0/ for more information on the XML Schema notation definition. Defining Types in web-services.xmlAfter you write the XML Schema for your UDT, you must add it to the <types> section of web-services.xml , as shown in the following code: <types> <!-- One user-defined type --> <xsd:schema... > : </xsd:schema> <!--Another user-defined type --> <xsd:schema... > : </xsd:schema> </types> You can define multiple UDTs within a schema section, but all UDTs in a schema section will share the same target namespace. Exactly when is an XML Schema actually used by the container because, in a sense, a UDT's type schema is already codified in its serializer class? The answer is: The schema is not used when a service call is made and fulfilled. It is used only in generating a service's WSDL file. However, a UDT schema is still required regardless of whether you do or do not need the WSDL file. Writing Serializers and DeserializersA serializer converts a Java object to XML representation, and a deserializer converts an XML document to a Java object. The characteristics of WebLogic/JAX-RPC serializers are as follows :
Now look at a sample serializer class for the Employee user-defined type, as shown in Listing 30.6. Listing 30.6 Sample Serializer Class for the Employee User-Defined Typepackage 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 EmployeeCodec extends weblogic.webservice.encoding.AbstractCodec { public void serialize(Object obj, XMLName name, XMLOutputStream writer, SerializationContext context) throws SerializationException { Employee emp = (Employee) obj; try { //outer start element <Employee> writer.add(ElementFactory.createStartElement(name)); //employee name element writer.add(ElementFactory.createStartElement("fullname")); writer.add(ElementFactory.createCharacterData(emp.getName())); writer.add(ElementFactory.createEndElement("fullname")); //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 </Employee> 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. Employee employee = new Employee(); 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; } The following is the serialize method signature: public void serialize(Object obj, XMLName name, XMLOutputStream writer, SerializationContext context) throws SerializationException The following are the parameters:
Now look at the deserializer method signature: public Object deserialize(XMLName name, XMLInputStream reader, DeserializationContext context) throws DeserializationException The following are the parameters:
When you're converting an embedded user-defined type, there is no way to look up its serializer/deserializer class. You just need to hard-code any embedded serializer class invocations. Defining Type Mappings in web-services.xmlThe last step in this process is to state which serializer/deserializer class should be called for each user-defined type. If WebLogic Server is generating your serializer class, its type mapping will also be generated for you. Otherwise, you need to add a type-mapping section to web-services.xml , as illustrated in Figure 30.12. The <type-mapping> element starts this section and encompasses one or more <type-mapping-entry> subelements, as shown in Listing 30.7. Listing 30.7 Type-Mapping Entry for User-Defined Type Employee<type-mapping> <type-mapping-entry class-name="examples.ws.Employee" xmlns:p1="java:examples.webservices" type="p1:Employee" serializer="examples.ws.EmployeeCodec" deserializer="examples.ws.EmployeeCodec" > </type-mapping-entry> </type-mapping> The following are the parameters:
You can specify multiple type-mapping entries, one for each of your user-defined classes. WebLogic Server presumes that your serializer class produces the correct Java object or XML document. It cannot verify correctness of your custom-created serializer class at build or deploy time. Also, ServiceGen will not copy your custom serializer/deserializer classes into the ear file, and if you specify an input type mapping file, it will assume that there is no need to generate serializer/deserializer classes for the types that are already mapped. |