Oracle® Application Server Advanced Web Services Developer's Guide 10g Release 3 (10.1.3) B25603-01 |
|
![]() Previous |
![]() Next |
This chapter describes the Custom Serialization Framework API. This API enables you to map and serialize Java value types between schema-defined XML types and Java value type classes that do not conform to the JAX-RPC value type requirements and cannot be handled by the default JAX-RPC serialization mechanism. This framework can also be used to implement custom mapping for the schema types that have default mappings. For example, you can use the framework to map xsd:dateTime
to the custom class com.hello.MyDate
instead of its default mapping java.util.Calendar
.
The Custom Serialization Framework API can be used to serialize Java value types for publishing a Web service top down, bottom up, or for generating Java value types from a schema to publish a Web service endpoint.
The built-in data type mappings that OracleAS Web Services supports can be found in Appendix D, "Mapping Java Types to XML and WSDL Types" and in the JAX-RPC 1.1 specification.
http://java.sun.com/webservices/jaxrpc/index.jsp
You do not need to create custom mappings for these Java value types."OC4J Support for Java Value Types" and Chapter 5.4 of the JAX-RPC 1.1 specification lists the requirements for Java value types.
If you want to expose a custom or non-standard Java value type in your Web service, OracleAS Web Services provides an API that lets you serialize them into an XML representation. The SOAPElementSerializer
interface in the oracle.webservices.databinding
package defines the interface for pluggable custom serializers for the OracleAS Web Services JAX-RPC implementation.
public interface SOAPElementSerializer
The serializer converts a Java object to an XML representation of SAAJ SOAPElement
. An instance of SOAPElementSerializer
serializes a Java object to a SOAPElement
and deserializes a SOAPElement
to a Java object.
The interface has the following methods.
public SOAPElement serialize(QName tagName, Object object)
Implements the serialize
method to marshall a Java object to an XML fragment of a SAAJ SOAPElement
instance. For this serializer implementation to be reusable, the implementation of serialize
should use the value of the tagName
parameter as the top level element tag name of the returned SOAPElement
instance. This is because an XML schema complexType
may be used for different element definitions.
public Object deserialize(SOAPElement element)
Implements the deserialize
method to unmarshal an XML fragment of a SAAJ SOAPElement
instance to a Java object.
public void init(Map prop)
Called by the Oracle Application Server Web Services runtime to indicate to a SOAPElementSerializer
that the SOAPElementSerializer
implementation instance is being placed into service. The init
method is called with an instance of Map
created from the init-param
elements specified under the corresponding serializer element in the Custom Type Mapping File. Both the key
type and value
type of the Map
instance are java.lang.String
.
An implementation of the interface requires a default no-argument constructor. For more information on the SOAPElementSerializer
interface, see the output of the Javadoc tool.
To use the custom serialization in your Web service, you provide an implementation of the SOAPElementSerializer
for your custom Java type value class. You must also provide the custom Java value type class and an XML configuration file that defines the mapping between the Java type value class and the XML schema type it should represent. You can find more information about these files in "Developing a Custom Serializer Implementation for a Java Type Value Class", "Defining a Custom Java Type Value Class", and "Creating an oracle-webservices Type Mapping Configuration".
The following sections describe how you can use custom serialization in top down, bottom up and schema-driven Web service development.
Using Custom Serialization in Top Down Web Service Development
Using Custom Serialization in Bottom Up Web Service Development
Using Custom Serialization in Schema-Driven Web Service Development
This section describes the steps in developing a custom serializer for a Java value type class. Typically, you start with the custom Java value type class that you want to express as an XML schema type. From there, you develop a custom serializer implementation of the class, the service endpoint interface, and its implementation. The oracle-webservices type mapping configuration XML file identifies the schema type that will be used to represent the custom Java type value class.
For top down Web service assembly, the service endpoint interface is generated from a WSDL. For bottom up Web service assembly, develop a Java service endpoint interface
Developing a Custom Serializer Implementation for a Java Type Value Class
Developing a Service Endpoint Interface that Uses a Java Type Value Class
This section illustrates a custom Java type value class that can be used to map to an XML schema type.
Note that the custom Java type class does not have to be Serializable
if the Web service is not going to be used with any J2EE features that require Serializable (such as EJB or JMS).
An empty constructor is not a requirement. The serializer implementation that you write programmatically constructs the instance of the corresponding Java type and populates the content of the Java object. This is why you want to use the custom serializer. It provides full control over the XML-to- Java and Java-to-XML mappings.
In JAX-RPC, xsd:dateTime
can be represented by either java.util.Calendar
or java.util.Date
. If you do not want to use either of these classes to represent a date, you can implement your own date class. Example 7-1 illustrates a custom Java value class, oracle.demo.custom_type.MyDate
, that can be used to represent a date.
Example 7-1 Sample Custom Java Type Value Class
package oracle.demo.custom_type; public class MyDate implements java.io.Serializable{ private String string; public MyDate(String s) { string = s; } public String getString() { return string; } public void setString(String string) { this.string = string; } }
To use a Java type value class in a Web service implementation and to allow an XML schema type to represent the class in a WSDL, you must provide an implementation of SOAPElementSerializer
for the Java type value class. The implementation will handle the serialization and deserialization between the Java type value class and the XML schema type.
Example 7-2 illustrates an implementation of SOAPElementSerializer
for the MyDate
value type class described in "Defining a Custom Java Type Value Class". The implementation will allow MyDate
to be used in the Web service endpoint interface and to handle serialization and deserialization between MyDate
and xsd:dateTime
. It will also allow the xsd:dateTime
schema type to represent the MyDate
class in the generated WSDL.
Example 7-2 Sample SOAPElementSerializer Implementation
package oracle.demo.custom_type; import oracle.webservices.databinding.SOAPElementSerializer; import javax.xml.soap.SOAPFactory; import javax.xml.soap.SOAPException; import javax.xml.soap.SOAPElement; import javax.xml.soap.Text; import javax.xml.namespace.QName; import java.util.Iterator; import java.util.Map; public class MyDateSerializer implements SOAPElementSerializer { private Map initParams; protected SOAPFactory soapFactory; public MyDateSerializer() { } public void init(Map prop) { initParams = prop; try { soapFactory = SOAPFactory.newInstance(); } catch (SOAPException e) { e.printStackTrace(); } } public Object deserialize(SOAPElement ele) { String str = ""; for( Iterator i = ele.getChildElements(); i.hasNext();) { Object obj = i.next(); if( obj instanceof Text ) { str += ( ((Text)obj).getValue() ); } } return new MyDate(str); } /** * The qname parameter must be used to create the top level element. */ public SOAPElement serialize(QName qname, Object obj) { SOAPElement xml = null; try { xml = soapFactory.createElement( qname.getLocalPart(), qname.getPrefix(), qname.getNamespaceURI()); xml.addTextNode( ((MyDate)obj).getString() ); } catch (SOAPException e) { e.printStackTrace(); } return xml; } }
Develop a Java service endpoint interface that will be exposed as the Web service. Example 7-3 and Example 7-4 illustrate a Java service endpoint interface and implementation that use the oracle.demo.custom_type.MyDate
class.
Note that to satisfy JAX-RPC requirements, the interface extends java.rmi.Remote
and the methods throw java.rmi.RemoteException
.
The MyDateServiceEndpoint
example also employs a user-defined bean type, MyDateBean
, as a parameter. MyDateBean
has two properties of MyDate
: beginDate
and endDate
. WebServicesAssembler will generate the schema for MyDateBean
because MyDate
is configured to use the custom serializer and MyDateBean
conforms to the JAX-RPC Value Type (Bean) patterns. Example 7-5 illustrates MyDateBean
.
Example 7-3 Sample Java Service Endpoint Interface
package oracle.demo.custom_type; import oracle.demo.custom_type.MyDate; public interface MyDateServiceEndpoint extends java.rmi.Remote { public MyDate echoMyDate(MyDate date) throws java.rmi.RemoteException; public MyDateBean echoMyDateBean(MyDateBean bean) throws java.rmi.RemoteException; }
Similarly, note that new methods defined in the implementation illustrated in Example 7-4 throw a java.rmi.RemoteException
.
Example 7-4 Sample Implementation of a Java Service Endpoint Interface
package oracle.demo.custom_type; public class MyDateServiceEndpointImpl implements MyDateServiceEndpoint { public MyDate echoMyDate(MyDate date) throws java.rmi.RemoteException { return date; } public MyDateBean echoMyDateBean(MyDateBean bean) throws java.rmi.RemoteException { return bean; } }
Example 7-5 illustrates a user-defined bean, MyDateBean
.
Example 7-5 Sample User-Defined Bean Type
package oracle.demo.custom_type; public class MyDateBean implements java.io.Serializable{ private MyDate beginDate; private MyDate endDate; public MyDate getBeginDate() { return beginDate; } public void setBeginDate(MyDate beginDate) { this.beginDate = beginDate; } public MyDate getEndDate() { return endDate; } public void setEndDate(MyDate endDate) { this.endDate = endDate; } }
Create an oracle-webservices type mapping configuration XML file to specify the XML schema type that will be used to represent the custom Java type value class. The contents of the <type-mapping>
element specify the XML schema type, the custom Java type value class, and the class that performs the custom serialization. Table 7-1 describes the contents of the <type-mapping>
element.
Table 7-1 Contents of the <type-mapping> Element
Attribute or Element Name | Description |
---|---|
java-class |
Attribute that identifies the Java type value class |
xml-type |
Attribute that identifies the XML schema type |
serializer-class |
Element that identifies the class that performs the custom serialization |
Use the ddFileName
argument to specify the oracle-webservices type mapping configuration to WebServicesAssembler.
The schema files oracle-webservices-10_0.xsd
and oracle-webservices-types-10_0.xsd
can be found in the oc4j-schemas.jar
file in the OC4J_HOME
/j2ee/home/lib/
directory.
Example 7-6 illustrates an oracle-webservices type mapping configuration that tells WebServicesAssembler to map the oracle.demo.custom_type.MyDate
class to xsd:dateTime
. The <serializer-class...>
element identifies the MyDateSerializer
class developed in Example 7-2.
Example 7-6 Sample oracle-webservices Type Mapping Configuration
<oracle-webservices> <webservice-description name="MyDateService"> <type-mappings xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <type-mapping java-class="oracle.demo.custom_type.MyDate" xml-type="xsd:dateTime"> <serializer class="oracle.demo.custom_type.MyDateSerializer"/> </type-mapping> </type-mappings> </webservice-description> </oracle-webservices>
By default, WebServicesAssembler does not configure custom types when generating the Web service client-side proxy. The generated proxy will use the default JAX-RPC Java value type-to-XML schema type mappings. For the MyDate
example presented in this section, this means that the default Java value type, java.util.Calendar
, will be used to represent xsd:dateTime
.
To configure custom types into your generated client proxy code, use the ddFileName
and classpath
arguments to genProxy
to specify the oracle-webservices type mapping configuration XML file. The classpath must include the value types and custom serializer. This will allow you to use the custom Java classes in your client-side proxy code. For the example in this section, providing the oracle-webservices type mapping configuration and its classpath when you generate the client-side proxy code enables you to use MyDate
instances for xsd:dateTime
in the Web service client-side proxy.
Example 7-7 assumes that the client-side proxy was generated with an oracle-webservices type mapping configuration XML file. Note the use of instances of MyDate
instead of the default mapping, Calendar
.
Example 7-7 Client-Side Proxy Code, Using Custom Java Type Values
ServiceFactory factory = ServiceFactory.newInstance(); MyDateService service = (MyDateService)factory.loadService(MyDateService.class); MyDateServiceEndpoint proxy = service.getMyDateServiceEndpointPort(); ((Stub)proxy)._setProperty( Stub.ENDPOINT_ADDRESS_PROPERTY, address ); MyDate req = new MyDate("Year:2004, Month:5, Date:24"); MyDate res = proxy.echoMyDate( req ); MyDate begin = new java.util.GregorianCalendar(1977, 5, 24); MyDate end = new java.util.GregorianCalendar(2004, 5, 24); ...
You can use the custom serializer to provide special Java value type-to-XML schema type mappings for a custom Java value type that you want to use in the service endpoint interface. This means that you can substitute a custom Java value type for a type that has a defined, default JAX-RPC mapping in the WSDL. WebServicesAssembler will process the custom Java value type and generate the correct code for the interface.
For example, you can change the default mapping of the schema-defined XML type xsd:dateTime
from java.util.Calendar
, to a custom Java value type class, hello.MyDate
. The custom serializer will allow you to generate the correct code for the service endpoint interface.
Other than providing the custom Java value type class, the SOAPElementSerializer
implementation, and the type mapping configuration file, this is very similar to the generic top down Web service development.
Developing a Web service that uses a custom serializer follows these general steps.
Run WebServicesAssembler with the genInterface
command and the custom serializer to generate the service endpoint interface with the custom type mappings.
Implement the service endpoint interface.
Run WebServicesAssembler again with the topdownAssemble
command to assemble the Web service and package it in an EAR.
The following sections describe these steps in greater detail.
Before you begin, provide the following files and information.
the custom Java value type class(es). For more information on custom Java value type classes, see, "Defining a Custom Java Type Value Class".
the implementation of the SOAPElementSerializer
interface for the custom Java value type class(es). For more information on implementing the SOAPElementSerializer
interface, see "Developing a Custom Serializer Implementation for a Java Type Value Class".
the WSDL that employs the custom Java value class(es).
the oracle-webservices type mapping configuration XML file
For more information on creating the oracle-webservices type mapping configuration, see "Creating an oracle-webservices Type Mapping Configuration".
The following steps describe how to use custom serialization in top down Web service development.
Provide the files described in the Prerequisites section as input to the WebServicesAssembler genInterface command. For example:
java -jar wsa.jar -genInterface -ddFileName myOracleWSDescriptor.xml -output build/src/service -wsdl wsdl/MyWSDL.wsdl -unwrapParameters false -packageName oracle.demo.customdoclit.service
The major difference between using a custom serializer and standard top down Web services development, is the presence of the ddFileName
argument. WebServicesAssembler will use the mappings specified in myOracleWSDescriptor.xml
to generate the service endpoint interface.
The WebServicesAssembler tool generates a Java interface for every port type specified in the WSDL and a Java Bean for each complex type. The myOracleWSDescriptor.xml
provides the custom mappings that are specified.
The name of the directory that stores the generated interface is based on the values of the output
and packageName
arguments. For this example, the generated interface is stored in build/src/service/oracle/demo/customdoclit/service
.
Compile the generated interface and type classes.
For example:
javac -destdir ${service.classes.dir} -excludes {javac.excludes} -path build/src/service -classpath ${wsdemo.common.class.path}
If you have set your CLASSPATH
to include the referenced libraries described in "Web Service Client APIs and JARs" in the Oracle Application Server Web Services Developer's Guide, you can omit the -classpath
argument. Otherwise, ensure that all of these libraries are contained in the path represented by ${wsdemo.common.class.path}
. If you are running an Ant example, the appropriate settings are made in the Ant build script.
Implement the service endpoint interface.
The implementation must have a method signature that matches every method in the generated Java interface.
Compile the service implementation.
For example, you can use the same command as in Step 2 if the interface source was generated in the same directory where the Impl
class was saved. If it was not, you must change the value of the path
argument.
Provide the service endpoint implementation, the WSDL, the custom Java value type class(es), the SOAPElementSerializer
implementation(s), and the type mapping configuration XML file as input to the WebServicesAssembler topdownAssemble
command.
java -jar wsa.jar - topDownAssemble -ddFileName myOracleWSDescriptor.xml -output ./build -wsdl wsdl/MyWSDL.wsdl -unwrapParameters false -packageName oracle.demo.customdoclit.service -className oracle.demo.customdoclit.service.MyEndpointImpl -appName MyWebService -ear ./build/MyWebService.ear
WebServicesAssembler generates and packages a deployable EAR file.
Use WebServicesAssembler to generate the Web services client-side proxy.
generate stubs (client proxies) for a J2SE Web service client by running the WebServicesAssembler tool with the genProxy
command, or
generate a service endpoint interface and a JAX-RPC mapping file for a J2EE Web service client by running the WebServicesAssembler tool with the genInterface
command.
For more information on generating and assembling client-side code, see "Assembling a J2EE Web Service Client" and "Assembling a J2SE Web Service Client" in the Oracle Application Server Web Services Developer's Guide.
To use MyDate
to represent xsd:dateTime
in your client-side proxy code, you must use ddFileName
to specify the oracle-webservices type mapping configuration XML file and classpath
to provide the path to the custom Java class MyDate
.
For example, the following command generates client proxies (stubs) that can be used for a J2SE client.
java -jar wsa.jar -genProxy -wsdl http://localhost:8888/custom_type_mydate/custom_type_mydate?WSDL -packageName oracle.demo.custom_type.mydate_with_ser -output ./build/src/client -ddFileName MyClientSideDD.xml -classpath ./build/classes/service
For information on coding a client-side proxy to use custom types, see "Using Custom Types in Client-Side Proxy Code".
You can use the custom serializer to provide special Java value type-to-XML schema type mappings of some specific Java value types used in the service endpoint interface. This means that you can use your own custom schema document which describes special Java value types-to-XML schema type mappings. When you provide special Java value type-to-XML schema type mappings, you do not want WebServicesAssembler to generate schema definitions for these types.
Because this is bottom up Web service development, WebServicesAssembler will generate a WSDL. In the WSDL, instead of generating the schema for the value types, WebServicesAssembler will import one or more schema documents you specify.
Before you begin, provide the following files and information.
the custom Java value type class(es). These classes must reside in the classpath. For more information on custom Java value type classes, see, "Defining a Custom Java Type Value Class".
the schema document that describes the custom Java value type(s)
the implementation of the SOAPElementSerializer
interface for the custom Java value type class(es). For more information on implementing the SOAPElementSerializer
interface, see "Developing a Custom Serializer Implementation for a Java Type Value Class".
the Java service endpoint interface that uses special custom Java value type class(es). For more information on Service Implementation Endpoints, see "Developing a Service Endpoint Interface that Uses a Java Type Value Class".
the oracle-webservices type mapping configuration XML file.
For more information on creating the oracle-webservices type mapping configuration file, see "Creating an oracle-webservices Type Mapping Configuration".
The following instructions use sample code to develop a Web service bottom up, that requires custom type mapping.
Provide the files described in the Prerequisites section as input to WebServicesAssembler assemble
command. For example:
java -jar wsa.jar -assemble -targetNamespace http://oracle.com/ws_demo/custom_type_mydate -typeNamespace http://oracle.com/ws_demo/custom_type_mydate -input ./build/classes/service -classpath ./build/classes/service -output ./dist -ddFileName ./MyOracleWSDescriptor.xml -interfaceName oracle.demo.custom_type.MyDateServiceEndpoint -className oracle.demo.custom_type.MyDateServiceEndpointImpl -serviceName MyDateService -appName custom_type_mydate
This command is similar to the standard bottom up Web services development except that the ddFileName
argument is used to specify the oracle-webservices type mapping configuration XML file. The input
argument contains the custom serializer implementation.
WebServicesAssembler generates the WSDL and packages a deployable EAR file.
Use WebServicesAssembler to generate the Web service client-side proxy. To use MyDate
to represent xsd:dateTime
in your client-side proxy code, you must use ddFileName
to specify the oracle-webservices type mapping configuration file and classpath
to provide the path to the custom Java class MyDate
.
java -jar wsa.jar -genProxy -wsdl http://localhost:8888/custom_type_mydate/custom_type_mydate?WSDL -packageName oracle.demo.custom_type.mydate_with_ser -output ./build/src/client -ddFileName ./MyClientSideDD.xml -classpath ./build/classes/service
For information on coding a client-side proxy to use custom types, see "Using Custom Types in Client-Side Proxy Code".
This release provides Ant tasks for Web service development. The following examples show how the WebServicesAssembler commands in the preceding examples can be rewritten as Ant tasks.
For the assemble
command, here is an example Ant task.
<oracle:assemble targetNamespace="http://oracle.com/ws_demo/custom_type_mydate" typeNamespace="http://oracle.com/ws_demo/custom_type_mydate" input="./build/classes/service" classpath="./build/classes/service" output="./dist" ddFileName="./custom_type_mydate_pdd.xml" serviceName="MyDateService" appName="custom_type_mydate" > <oracle:porttype interfaceName="oracle.demo.custom_type.MyDateServiceEndpoint" className="oracle.demo.custom_type.MyDateServiceEndpointImpl"> </oracle:porttype> </oracle:assemble>
For the genProxy
command, here is an example Ant task.
<oracle:genProxy wsdl="http://localhost:8888/custom_type_mydate/custom_type_mydate?WSDL" packageName="oracle.demo.custom_type.mydate_with_ser" output="./build/src/client" ddFileName="./MyOracleClientDD.xml" classpath="./build/classes/client" />
In schema-driven Web service development, you invoke WebServicesAssembler with the schema document to generate the Java value type classes (beans) before you assemble the Web service.
Because the Java value classes generated from a schema complex type conform to the JAX-RPC value type (bean) pattern requirement, a custom serializer is not necessary. If you are satisfied with the default mapping provided by JAX-RPC, you do not need to implement the a custom serializer.
However, if you want to change the default marshaling between XML and Java, then you must implement the custom serializer for the generated Java value type classes.
Assembling a Web service from a schema follows these general steps.
Run WebServicesAssembler with the schema document to generate the Java Value Type classes, the standard JAX-RPC mapping file, and the oracle-webservices type mapping configuration XML file (custom-type-mappings.xml
).
Develop your own service endpoint interface and its implementation.
If you are not happy with the default marshalling for one or more classes, write a custom serializer to implement your own marshalling logic.
Edit the oracle-webservices type mapping configuration XML file to include the custom serializer. You can also substitute an alternative Java class for any of the generated Java value type classes.
Run WebServicesAssembler again, this time, to generate a WSDL document. Input the JAX-RPC mapping file, the edited oracle-webservices type mapping configuration XML file, the schema document, the generated Java value types, and the service endpoint interface and its implementation. Also include the custom serializer, if one was created.
The generated WSDL will import the specified schema instead of generating one from the Java value types.
WebServicesAssembler will assemble a deployable EAR with a generated WSDL and all of the implementation files and deployment descriptors to enable a Web service.
Before you begin, provide the following files and information.
the schema document. "Sample Schema Document" lists a schema document that defines types can be exposed through a service endpoint interface.
the definition of a custom Java type that will represent the complex type in the schema.
if you are providing custom default marshalling logic, you must also provide a custom serializer to implement your own marshalling logic.
This section describes how to develop a Web service from a XML schema that contains complex types. It also describes how to provide a custom serializer to handle serialization and deserialization between the complex type and a Java custom type.
"Sample Schema Document" describes the schema used in this example. The schema contains two complex types: InitItem
and StringItem
.
The section assumes that you want to use the custom Java type, oracle.webservices.examples.customtypes.MyInitItem
, described in "Java Custom Type Implementation", to represent the InitItem
complex type in the schema. To do this, you must provide a custom serializer to handle serialization and deserialization between the Java instance of MyInitItem
and XML instance of InitItem
.
For the complex schema type StringItem
, the section assumes that you want to use the default JAX-RPC value type data binding mechanism to generate a value type class to represent the complex type. It also assumes that you want to do custom formatting on the XML instances of xsd:string
. To do this, a custom serializer will be created to handle serialization and deserialization between Java instance of java.lang.String
and the XML instance of xsd:string
.
The following steps describe how to develop this Web service.
Run WebServicesAssembler with the schema document to generate the Java value types and the standard JAX-RPC mapping file.
java -jar wsa.jar -genValueTypes -packageName oracle.webservices.examples.customtypes -schema ./MySchema.xsd -output ./build/src/types
The schema document used in this command is described in "Sample Schema Document". The genValueTypes
command generates two classes from this schema InitItem
and StringItem
. The InitItem
class is not needed since you will be replacing it with your custom type class.
This command also generates a JAX-RPC mapping file, jaxrpc-mappings.xml
, which describes the binding of the generated value type classes, and a custom-type-mappings.xml
file to record which value type classes are generated from an existing schema file. The custom-type-mappings.xml
file is a generated server-side instance of the OracleAS Web Services type mapping configuration XML file.
Compile the generated value types to the /build/classes/service
directory.
The genValueTypes
command does not compile any classes. You must perform this step yourself. Note that you can either delete the IntItem
class or exclude it from your compilation. This is because you will be providing a custom serializer for it.
Implement the custom serializers. In this example, two custom serializers are required: a serializer to map between oracle.webservices.examples.customtypes.MyIntItem
and InitItem
and a serializer to map between java.lang.String
and xsd:string
.
"Defining a Serializer Implementation with Marshalling Logic" contains a sample implementation of these serializers.
Develop the Service Endpoint interface and its implementation.
"Developing a Service Endpoint Interface and Implementation" provides a sample implementation of a service endpoint interface that uses oracle.webservices.examples.customtypes.MyInitItem
and the generated Java value type classes.
Edit the custom-type-mappings.xml
file to include an alternative Java class and to include the custom serializer. In Step 1, the custom-type-mappings.xml
file was generated into the output directory /build/src/types
.
Edit the file to use oracle.webservices.examples.customtypes.MyInitItem
and its custom serializer, and to use the oracle.webservices.examples.customtypes.MyStringSerializer
custom serializer. For details about adding the alternative Java class and custom serializers, see "Editing the Generated oracle-webservices Type Mapping Configuration XML File".
Run the WebServicesAssembler tool again, this time with the assemble
command, to generate the WSDL and a deployable EAR file. Use the following items as input.
the XML schema document (MySchema.xsd
)
the value type classes generated in Step 1 (contained in the build/classes.service
directory)
the JAX-RPC mapping file (jaxrpc-mappings.xml
) generated in Step 1
the compiled custom serializer classes created in Step 2 (contained in the build/classes.service
directory)
the service endpoint interface and its implementation (MyEndpointIntf
and MyEndpointImpl
) created in Step 3
the modified custom type mapping file MyCustomTypeMappings.xml
created in Step 4
Following is a sample assemble
command.
java -jar wsa.jar -assemble -schema ./MySchema.xsd -mappingFileName ./build/src/types/jaxrpc-mappings.xml -input ./build/classes/service -classpath ./build/classes/service -output ./dist -ddFileName ./MyCustomTypeMappings.xml -interfaceName oracle.webservices.examples.customtypes.MyEndpointIntf -className oracle.webservices.examples.customtypes.MyEndpointImpl -appName MyCustomTypes
The input directory, build/classes/service
, contains the compiled value type classes generated in Step 1 and the compiled custom serializer files created in Step 2. The class name of each custom serializer is listed in the oracle-webservices type mapping file MyCustomTypeMappings.xml
. Use the ddFileName
argument to specify this file.
The WebServicesAssembler tool assembles a deployable EAR file in the /dist
directory. The generated WSDL in the EAR file imports the schema document.
Note that this command is similar to normal Java-to-WSDL, or bottom up Web services assembly.
Deploy the service and bind the application.
Deploy EAR files in the standard manner into a running instance of OC4J. For more information on deploying EAR files, see the Oracle Containers for J2EE Deployment Guide. The following is a sample deployment command:
java -jar <OC4J_HOME>/j2ee/home/admin_client.jar deployer:oc4j:localhost:port <user> <password> -deploy -file ./dist/MyCustomTypes.ear -deploymentName MyCustomTypes -bindWebApp default-web-site
Develop the client.
generate stubs (client proxies) for a J2SE Web service client by running the WebServicesAssembler tool with the genProxy
command, or
generate a service endpoint interface and a JAX-RPC mapping file for a J2EE Web service client by running the WebServicesAssembler tool with the genInterface
command.
For an example of developing a J2SE client-side proxy that uses oracle.webservices.examples.customtypes.MyInitItem
to represent InitItem
and the custom serializer to handle the marshalling and unmarshaling of data between the client and server, see "Developing a Client for Custom Type Mapping and a Custom Serializer".
Example 7-8 illustrates the MySchema.xsd
schema document that provides types that will be exposed as a service endpoint interface. The InitItem
and StringItem
are complex types.
Example 7-8 Sample Schema Document
<schema targetNamespace="http://examples.webservices.oracle/customtypes" xmlns:tns="http://ws.oracle.com/types" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://www.w3.org/2001/XMLSchema"> <complexType name="IntItem"> <sequence> <element name="name" type="xsd:string"/> <element name="value" type="xsd:int"/> </sequence> </complexType> <complexType name="StringItem"> <sequence> <element name="name" type="xsd:string"/> <element name="value" type="xsd:string"/> </sequence> </complexType> </schema>
Example 7-9 illustrates an implementation of the Java custom type oracle.webservices.examples.customtypes.MyIntItem
. This type will be used to represent the InitItem
complexType illustrated in Example 7-8.
Example 7-9 Implementation of the Custom Java Type InitItem
/** * MyIntItem does not have the JAXRPC Value type (Bean) pattern. */ package oracle.webservices.examples.customtypes; public class MyIntItem { private String name; private int value; public MyIntItem(String name, int value) { this.name = name; this.value = value; } public String itemName() { return name; } public int itemValue() { return value; } }
This section provides examples of the files you must provide and the tasks you must perform if you want to implement a custom serializer that contains custom marshalling logic.
This section provides an example of a serializer implementation that contains custom marshalling and unmarshalling logic. It assumes that you want to use the oracle.webservices.examples.customtypes.MyInitItem
custom Java data type in your Web service to represent the InitItem
complex type in the schema document. It also assumes that you want to provide custom marshalling and unmarshaling logic for the class. To provide the custom logic, you must implement a custom serializer.
Example 7-10 provides a sample custom serializer implementation for MyInitItem
. To provide serialization capabilities, the custom serializer, MyInitItemSerializer
, implements the SOAPElementSerializer
class and implements its serialize
and deserialize
methods.
Example 7-10 Custom Serializer Implementation to Map MyInitItem to InitItem
package oracle.webservices.examples.customtypes; import java.util.Iterator; import java.util.Map; import javax.xml.namespace.QName; import javax.xml.soap.SOAPElement; import javax.xml.soap.SOAPException; import javax.xml.soap.SOAPFactory; import javax.xml.soap.Text; import oracle.webservices.databinding.SOAPElementSerializer; public class MyIntItemSerializer implements SOAPElementSerializer { private Map config; private SOAPFactory soapFactory; public void init(Map initParams) { config = initParams; try { soapFactory = SOAPFactory.newInstance(); } catch (SOAPException e) { e.printStackTrace(); } } public SOAPElement serialize(QName tagName, Object object) { MyIntItem myIntItem = (MyIntItem)object; SOAPElement xml = null; try { xml = soapFactory.createElement( tagName.getLocalPart(), tagName.getPrefix(), tagName.getNamespaceURI()); SOAPElement name = soapFactory.createElement( "name", tagName.getPrefix(), tagName.getNamespaceURI()); name.addTextNode(myIntItem.itemName()); SOAPElement value = soapFactory.createElement( "value", tagName.getPrefix(), tagName.getNamespaceURI()); value.addTextNode(String.valueOf(myIntItem.itemValue())); xml.addChildElement(name); xml.addChildElement(value); } catch (SOAPException e) { e.printStackTrace(); } return xml; } private String getTextContent(SOAPElement element) { String str = ""; for (Iterator i = element.getChildElements(); i.hasNext();) { Object obj = i.next(); if (obj instanceof Text) { str += (((Text) obj).getValue()); } } return str; } public Object deserialize(SOAPElement element) { String name = null; int value = -1; for (Iterator i = element.getChildElements(); i.hasNext();) { Object obj = i.next(); if (obj instanceof SOAPElement) { SOAPElement child = (SOAPElement)obj; if (child.getLocalName().equals("name")) { name = MyStringSerializer.createMyString(getTextContent(child)); } if (child.getLocalName().equals("value")) { value = Integer.parseInt(getTextContent(child)); } } } return new MyIntItem(name, value); } }
Example 7-11 provides a sample custom serialization implementation to map java.lang.String
to xsd:string
. To provide serialization capabilities, the custom serializer, MyStringSerializer
, implements the SOAPElementSerializer
class and implements its serialize
and deserialize
methods. It also provides a placeholder where you can enter your own string formatting or validation code.
Example 7-11 Custom Serializer Implementation to Map java.lang.String to xsd:string
package oracle.webservices.examples.customtypes; import java.util.Iterator; import java.util.Map; import javax.xml.namespace.QName; import javax.xml.soap.SOAPElement; import javax.xml.soap.SOAPException; import javax.xml.soap.SOAPFactory; import javax.xml.soap.Text; import oracle.webservices.databinding.SOAPElementSerializer; public class MyStringSerializer implements SOAPElementSerializer { private Map config; private SOAPFactory soapFactory; public void init(Map initParams) { config = initParams; try { soapFactory = SOAPFactory.newInstance(); } catch (SOAPException e) { e.printStackTrace(); } } public SOAPElement serialize(QName tagName, Object object) { String value = (object == null) ? "null" : object.toString(); SOAPElement xml = null; try { xml = soapFactory.createElement( tagName.getLocalPart(), tagName.getPrefix(), tagName.getNamespaceURI()); xml.addTextNode(value); } catch (SOAPException e) { e.printStackTrace(); } return xml; } public Object deserialize(SOAPElement element) { String value = ""; for (Iterator i = element.getChildElements(); i.hasNext();) { Object obj = i.next(); if (obj instanceof Text) { value += (((Text) obj).getValue()); } } return createMyString( value ); } static public String createMyString(String input) { // You can add your own formating or validation logic here return input.trim().toUpperCase(); } }
This section illustrates a service endpoint implementation that uses a different Java data type than the one in the generated schema document.
Example 7-14 provides a sample service endpoint interface, oracle.webservices.examples.customtypes.MyEndpointIntf
, and Example 7-15 provides its implementation, oracle.webservices.examples.customtypes.MyEndpointImpl
. Note that the interface extends java.rmi.Remote
and its methods throw java.rmi.RemoteException
. Also note that the getItems
methods in the interface and implementation get items of type MyInitItem
instead of the InitItem
value type class generated from the schema document.
Example 7-12 illustrates the generated schema document that defines the Inititem
and StringItem
types.
Example 7-12 Schema Definitions of InitItem and StringItem
<?xml version = '1.0' encoding = 'UTF-8'?> <schema targetNamespace="http://examples.webservices.oracle/customtypes" xmlns:tns="http://ws.oracle.com/types" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://www.w3.org/2001/XMLSchema"> <!-- this complex type is mapped to a custom value type using a custom serializer --> <complexType name="IntItem"> <sequence> <element name="name" type="xsd:string"/> <element name="value" type="xsd:int"/> </sequence> </complexType> <!—xsd:string is mapped to a custom value type using a custom serializer --> <complexType name="StringItem"> <sequence> <element name="name" type="xsd:string"/> <element name="value" type="xsd:string"/> </sequence> </complexType> </schema>
To assemble a Web service that will expose the methods that use oracle.webservices.examples.customtypes.MyInitItem
data types, you must also replace the InitItem
entry in the oracle-webservices type mapping configuration XML file with MyInitItem
. This is described in "Editing the Generated oracle-webservices Type Mapping Configuration XML File".
Optionally, if you want to provide custom marshalling and unmarshaling for MyInitItem
data, you must implement a custom serializer for this class. This is described in "Defining a Serializer Implementation with Marshalling Logic".
Example 7-13 illustrates the definition of the Items
custom type class. This class will be used in the service endpoint implementation. the items class uses two value types, MyIntItem
and StringItem
. The MyInitItem
class is a custom class that you need to provide a custom serializer. The StringItem
class is generated from the schema with custom mapping of String
. Both MyIntItem
and StringItem
are described by an existing schema document.
Example 7-13 Definition of the Items Class
package oracle.webservices.examples.customtypes; public class Items { private MyIntItem[] myIntItem; private StringItem[] stringItem; public Items() { } public Items(MyIntItem[] intItem, StringItem[] strItem) { myIntItem = intItem; stringItem = strItem; } public MyIntItem[] getMyIntItem() { return myIntItem; } public void setMyIntItem(MyIntItem[] intItem) { myIntItem = intItem; } public StringItem[] getStringItem() { return stringItem; } public void setStringItem(StringItem[] strItem) { stringItem = strItem; } }
Example 7-14 illustrates the service endpoint interface for MyEndpointIntf
. Notice that the getItems
method uses the MyInitItem
data type instead of the InitItem
type defined by the schema. As required by JAX-RPC, the interface extends java.rmi.Remote
and the methods throw java.rmi.RemoteException
.
Example 7-14 Service Endpoint Interface for MyEndpointIntf
package oracle.webservices.examples.customtypes; import java.rmi.Remote; import java.rmi.RemoteException; public interface MyEndpointIntf extends Remote { public Items getItems( MyIntItem[] myInts, StringItem[] myStrings ) throws RemoteException; }
Example 7-15 illustrates the implementation of the MyEndpointIntf
interface.
The genValueTypes
command creates a generated instance of the oracle-webservices type mapping configuration XML file, custom-type-mappings.xml
. This file records the Java value type classes that are generated from an existing schema document. When you generate a WSDL from a service endpoint interface, you can use this file as input to WebServicesAssembler to avoid regenerating the schema definition for these pre-generated classes.
If you want to use an alternative Java class to represent a complex type in the schema instead of the class generated from the schema document, then you must edit the contents of the appropriate <type-mapping>
element in the custom type mapping file. You can also edit the element to include a custom serializer for the alternative class.
The sample custom type mapping file in Example 7-16 assumes that the genValueTypes
command used the schema in Example 7-8 to generate the Java value types.
Example 7-16 Custom Type Mapping File, with Pre-Generated Value Type Classes
<?xml version="1.0" encoding="UTF-8"?> <oracle-webservices> <webservice-description name="unknown"> <type-mappings xmlns:typens0="http://examples.webservices.oracle/customtypes" xmlns:typens2="http://www.w3.org/2001/XMLSchema" > <type-mapping java-class="oracle.webservices.examples.customtypes.StringItem" xml-type="typens0:StringItem"> </type-mapping> <type-mapping java-class="oracle.webservices.examples.customtypes.IntItem" xml-type="typens0:IntItem"> </type-mapping> <type-mapping java-class="int" xml-type="typens2:int"> </type-mapping> <type-mapping java-class="java.lang.String" xml-type="typens2:string"> </type-mapping> </type-mappings> </webservice-description> </oracle-webservices>
To use oracle.webservices.examples.customtypes.MyInitItem
and its custom serializer oracle.webservices.examples.customtypes.MyInitItemSerializer
instead of the generated InitItem
class, and to use the custom serializer oracle.webservices.examples.customtypes.MyStringSerializer
for java.lang.String
, you must edit the custom-type-mappings.xml
file.
Replace the instance of InitItem
.
Replace the instance of InitItem
with oracle.webservices.examples.customtypes.MyInitItem
in the appropriate <type-mapping>
element.
Add a <serializer>
element to <type-mapping>
to specify the custom serializer class, oracle.webservices.examples.customtypes.MyInitItemSerializer
for MyInitItem
.
For example, the original <type-mapping>
element in custom-type-mappings.xml
:
<type-mapping java-class="oracle.webservices.examples.customtypes.IntItem" xml-type="typens0:IntItem"> </type-mapping>
should now look like this:
<type-mapping java-class="oracle.webservices.examples.customtypes.MyIntItem" xml-type="typens0:IntItem"> <serializer class="oracle.webservices.examples.customtypes.MyInitItemSerializer"/> </type-mapping>
Edit the <type-mapping>
element for the mapping of java.lang.String
to xsd:string
. The entry should look like this:
<type-mapping> java-class="java.lang.String" xml-type="xsd:string"> <serializer class="oracle.webservices.examples.customtypes.MyStringSerializer"/> </type-mapping>
Rename the file MyCustomTypeMappings.xml
and copy it to the root directory of the project.
Example 7-17 illustrates the edited version of the type mapping file. The type-mapping element for StringItem
is retained in the file in order to avoid regenerating the schema definition for this pre-generated class.
The namespace prefix declaration (xmlns:xsd="http://www3w.org/2001/XMLSchema"
) is added so that it can be used in the type mapping configuration file. In the edited type mapping configuration file, you must refer to the string
schema type defined in the schema namespace (http://www3w.org/2001/XMLSchema
). Defining the prefix, xsd
, enables you to use xsd:string
as the "qualified name" of the schema type string
.
Example 7-17 Custom Type Mapping File, with Edited Value Type Classes
<?xml version="1.0" encoding="UTF-8"?> <oracle-webservices> <webservice-description name="unknown"> <type-mappings xmlns:typens0="http://examples.webservices.oracle/customtypes" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <type-mapping java-class="java.lang.String" xml-type="xsd:string"> <serializer class="oracle.webservices.examples.customtypes.MyStringSerializer"/> </type-mapping> <type-mapping java-class="oracle.webservices.examples.customtypes.MyIntItem" xml-type="typens0:IntItem"> <serializer class="oracle.webservices.examples.customtypes.MyIntItemSerializer"/> </type-mapping> <type-mapping java-class="oracle.webservices.examples.customtypes.StringItem" xml-type="typens0:StringItem"> </type-mapping> </type-mappings> </webservice-description> </oracle-webservices>
To generate J2SE client-side proxy files, use the WebServicesAssembler genProxy
command:
java -jar wsa.jar -genProxy -wsdl http://localhost:8888/MyCustomTypes/MyCustomTypes?WSDL -output ./build/src/client
Because WebServicesAssembler does not configure custom types when generating the Web services client-side proxy, the generated proxy will try to generate Value Type (Bean) classes by default to represent the schema types InitItem
and StringItem
. Then you can write your client program using the generated proxy code.
Example 7-18 Client Program Using Default Generated Value Types
package oracle.webservices.examples.customtypes; import javax.xml.rpc.ServiceFactory; import javax.xml.rpc.Stub; import examples.webservices.oracle.customtypes.*; public class MyCustomTypesClient { public static final String DEFAULT_URL ="http://localhost:8888/MyCustomTypes/MyCustomTypes"; public static void main(String[] args) throws Exception { String address = DEFAULT_URL; ServiceFactory factory = ServiceFactory.newInstance(); MyCustomTypes service = (MyCustomTypes) factory.loadService(MyCustomTypes.class); MyEndpointIntf proxy = (MyEndpointIntf)service.getPort(MyEndpointIntf.class); ((Stub) proxy)._setProperty(Stub.ENDPOINT_ADDRESS_PROPERTY, address); IntItem int1 = new IntItem(); int1.setName("int1"); int1.setValue(123); IntItem int2 = new IntItem(); int2.setName("int2"); int2.setValue(456); IntItem[] ints = { int1, int2 }; StringItem str1 = new StringItem(); str1.setName("string1"); str1.setValue("foo"); StringItem str2 = new StringItem(); str2.setName("string2"); str2.setValue("coo"); StringItem[] strings = { str1, str2 }; Items items = proxy.getItems(ints, strings); System.out.println("items.getMyIntItem : " + items.getMyIntItem().length); System.out.println("items.getStringItem: " + items.getStringItem(). length); } }
The MyInitItem
class was used in developing the Items
class aforementioned. The Items
type was developed to be a return value of the service endpoint implementation. The MyInitItemSerializer
and MyStringTypeSerializer
are used in the OracleAS Web Services runtime. While WebServicesAssembler is assembling the deployable EAR file, the edited Type Mapping Configuration XML File is used to create a deployment descriptor inside the ear so that these custom serializes will be correctly used in the runtime.
You can use a custom serializer in the Web service client-side proxy. This enables you to use the custom data types defined for the service in your client code. This section describes how to create a Web service client-side proxy that uses the MyIntItemSerializer
(described in Example 7-10) to map IntItem
to MyIntItem
and MyStringSerializer
(described in Example 7-11) to map the string
type. To do this, you must use a client side version of the oracle-webservices type mapping configuration file, MyCustomTypeMappings.xml
(described in Example 7-17) as input to genProxy
.
The following steps summarize the process of adding a custom serializer to a client proxy:
Provide a client version of the server-side custom type mapping file.
For more information on this step, see "Editing the Server Side Custom Type Mapping File".
Generate the Web service client proxy with the WebServicesAssembler genProxy
command.
For more information on this step, see "Generating the Web Service Client Side Proxy".
Write the Web service client.
For more information on this step, see "Writing a Web Service Client with Custom Datatypes".
The custom type mapping file used on the client side must conform to the oracle-webservices-client-10_0.xsd
schema. To provide a custom type mapping file for the client, you can use the service side custom type mapping file as a template. Edit the file to provide the appropriate client side root elements:
Replace the <oracle-webservices>
element with <oracle-webservices-clients>
.
Replace the <webservice-description>
element and any attributes it might contain with <webservice-client>
.
After making these edits, the client side custom type mapping file (in this case, MyCustomTypeMappings.xml
) should look like the code in Example 7-19.
Example 7-19 Client Side Custom Type Mapping File
<?xml version="1.0" encoding="UTF-8"?> <oracle-webservice-clients> <webservice-client> <type-mappings xmlns:typens0="http://examples.webservices.oracle/customtypes" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <type-mapping java-class="java.lang.String" xml-type="xsd:string"> <serializer class="oracle.webservices.examples.customtypes.MyStringSerializer"/> </type-mapping> <type-mapping java-class="oracle.webservices.examples.customtypes.MyIntItem" xml-type="typens0:IntItem"> <serializer class="oracle.webservices.examples.customtypes.MyIntItemSerializer"/> </type-mapping> <type-mapping java-class="oracle.webservices.examples.customtypes.StringItem" xml-type="typens0:StringItem"> </type-mapping> </type-mappings> </webservice-client> </oracle-webservice-clients>
Generate the Web services client-side proxy with the WebServicesAssembler genProxy
command. Use the ddFileName
argument to provide the client side custom type mapping file as input. The genProxy
command will create client proxy classes will use your serializers.
The following sample genProxy
command uses the client side custom type mapping file MyCustomTypeMappings.xml
as input.
java -jar wsa.jar -genProxy -wsdl http://localhost:8888/MyCustomTypes/MyCustomTypes?WSDL -ddFileName ./MyCustomTypeMappings.xml -output ./build/src/client
When you write your client program using the generated proxy code, you can call MyIntItem
instead of IntItem
. This is illustrates in Example 7-20.
Example 7-20 Client Code Incorporating Custom Type Mapping and Custom Serializer
ServiceFactory factory = ServiceFactory.newInstance(); MyCustomTypes service = (MyCustomTypes) factory.loadService(MyCustomTypes.class); MyEndpointIntf proxy = (MyEndpointIntf)service.getPort(MyEndpointIntf.class); ((Stub) proxy)._setProperty(Stub.ENDPOINT_ADDRESS_PROPERTY, address); MyIntItem int1 = new MyIntItem("int1", 123); MyIntItem int2 = new MyIntItem("int2", 456); MyIntItem [] ints = { int1, int2 }; StringItem str1 = new StringItem(); str1.setName("string1"); str1.setValue("foo"); StringItem str2 = new StringItem(); str2.setName("string2"); str2.setValue("coo"); StringItem[] strings = { str1, str2 }; Items items = proxy.getItems(ints, strings);
For more information on:
assembling a Web service top down, see "Assembling a Web Service from a WSDL" in the Oracle Application Server Web Services Developer's Guide.
using Java classes to assemble a Web service, see "Assembling a Web Service with Java Classes" in the Oracle Application Server Web Services Developer's Guide.
using EJBs to assemble a Web service, see "Assembling a Web Service with EJBs" in the Oracle Application Server Web Services Developer's Guide.
using JMS topics and destinations to assemble a Web service, see "Assembling Web Services with JMS Destinations" in the Oracle Application Server Web Services Developer's Guide.
using database resources, such as PL/SQL packages, SQL queries, DML statements, Oracle Streams AQ, or server-side Java classes, to assemble a Web service, see "Assembling Database Web Services" in the Oracle Application Server Web Services Developer's Guide.
assembling a J2EE Web Service client, see "Assembling a J2EE Web Service Client" in the Oracle Application Server Web Services Developer's Guide.
assembling a J2SE Web service client, see "Assembling a J2SE Web Service Client" in the Oracle Application Server Web Services Developer's Guide.
using WebServicesAssembler commands to assemble Web service artifacts, see "Using WebServicesAssembler" in the Oracle Application Server Web Services Developer's Guide.
JARs that are necessary to compile client code, see "Web Service Client APIs and JARs" in the Oracle Application Server Web Services Developer's Guide,
the contents of the oracle-webservices.xml
deployment descriptor which contains the Web services management configuration, see "Packaging and Deploying Web Services" in the Oracle Application Server Web Services Developer's Guide.