rss
SOATUBE
Oracle
Custom Search SOABYTE here

Friday, June 25, 2010

Java-to-XML Bindings

















Understanding WSIF

To invoke a Java resource from BPEL, you will need to use data from BPEL variables, which are sent as input parameters to Java resources, and to send data from Java back to BPEL. BPEL variables are XML, whereas Java variables are not; therefore, you need a mapping between XML and Java.
To handle XML data from Java you have several options:
  • Handle XML manually through the DOM (Document Object Model) API: This way, the input and output parameters of the corresponding Java methods are of type Element from the World Wide Web Consortium Document Model Object API for Java. Use DOM methods to manipulate XML directly.
  • Use automated Java-to-XML bindings: Java-to-XML binding enables automatic conversion of XML Schema types to Java types. To achieve this, interfaces and a set of Java classes are generated through which you manipulate the XML. This way, XML is hidden, and you can use it through interfaces (such as JavaBeans). Here you have two options:
    • Oracle BPEL Process Manager: Supports default Java-to-XML bindings through the use of XML façades.
    • Use of custom Java serializers: Oracle already provides custom serializers that support JAXB (Java API for XML Bindings), XML beans, and Axis beans. You can also write your own serializers.
Let's have a look at the XML façades first.

XML façades

XML façades are Oracle BPEL Process Manager's original Java-to-XML binding for WSIF and are an integral part of the product. XML façades are a set of Java interfaces and classes through which you can access and modify XML data stored in BPEL variables in a relatively easy way using get/set methods. In this manner, you are not required to manipulate XML directly. Furthermore, the XML is hidden behind the façade, and you can manipulate the data through regular Java interfaces, a concept known as XML serialization. The idea behind XML façades is to provide support for basic data types through mapping to built-in types and to generate Java classes from XML Schemas for complex types.
The automatic mapping for the basic data types between XML Schema and Java types is shown here:

XML Schema Type
Java Types
xs:string
  • java.lang.String
    • char
    • java.lang.Character
xs:int, xs:integer
  • int
  • java.lang.Integer
  • java.math.BigInteger
xs:long
  • long
  • java.lang.Long
xs:short
  • short
  • java.lang.Short
xs:float
  • float
  • java.lang.Float
xs:double
  • double
  • java.lang.Double
  • java.math.BigDecimal
xs:byte
  • byte
  • java.lang.Byte
xs:Boolean
  • boolean
  • java.lang.Boolean
dateTime
java.util.Calendar
date
java.util.Date
As you can see, most of the simple types can be mapped to either primitive or object types. This is useful as you can adapt the mapping to the actual types used in your Java code. In addition to simple types, you also need a way to map complex types, whether those defined in the <types> section of the WSDL or in the external XML Schema (XSD) files. For example, in your book rating Web service WSDL, you'll notice an operation that takes as input the BookRatingRequestMessage, which is of type BookDscType. The BookDscType complex XML type is used for the BookRatingRequestMessage and for the correspondingBookRatingRequest BPEL variable:
<xs:schema elementFormDefault="qualified" 
            targetNamespace="http://oracle.com/service/bookrating/">

   <xs:complexType name="BookDscType">
     <xs:sequence>
       <xs:element name="Title" type="xs:string" /> 
       <xs:element name="ISSN" type="xs:string" /> 
       <xs:element name="Publisher" type="xs:string" /> 
       <xs:element name="Authors" type="xs:string" /> 
     </xs:sequence>
   </xs:complexType>

</xs:schema>
The XML façade for this complex XML type provides an interface and a class through which you can access the elements (title, ISSN, publisher, authors) using Java getter methods. The XML façade also allows you to modify the element data using setter methods.
An XML façade for this variable consists of an interface (IBookDscType), and a class (BookDscType) that provides the following methods:
  • getTitle() and setTitle()
  • getISSN() and setISSN()
  • getPublisher() and setPublisher()
  • getAuthors() and setAuthors()
There is also a factory class (BookDscTypeFactory) through which you can create the IBookDscType using the createFacade()method. XML façade makes the code simpler and easier to maintain; this is particularly true for larger variables with many member fields.
Oracle BPEL Process Manager provides a schema compiler utility called schemac. Using this utility you can generate XML façades. To generate the XML façade for BookRating.wsdl, use the following command line:

Z:\WSIF\2_JavaBindingClass>schemac BookRating.wsdl
----------------------------------------------------
Oracle XML Schema Processor Version 10.1.2.0.0
http://otn.oracle.com/bpel
Copyright (c) 2002-2004 - Oracle
(type schemac -help for help)
----------------------------------------------------

schemac> parsing schema file 'BookRating.wsdl' ...
schemac> Loaded schemas from wsdl located at BookRating.wsdl
schemac> generating XML business document ...
schemac> compiling XML business documents ...
Schemac completed successfully.

Z:\WSIF\2_JavaBindingClass>
To use these classes from Java resources, you will need to compile them into the C:\OraBPELPM_1\ integration\orabpel\system\classes directory where the BPEL server can access them.
The schemac utility has several options. You can use the -d switch to define the directory where the generated façade classes should be stored. To see the façade source code, use the -trace option. The schemac utility can also be used to generate XML Schemas out of Java classes. This is useful if you would like to adapt the service interface to an existing Java resource. You have to use the -R switch and provide the Java class name without the extension.

Developing the Java class

To replace the book rating Web service with a Java class without modifying BPEL, you need a Java class that has the same interface (contract) as the original book rating Web services. This means that the Java class has to provide the operations with the identical functionality, and that operations have to accept the same parameters and return the same result type—but the operation name needn't be identical.
Looking at the original WSDL, you'll see that the book rating Web service provides an operation called BookRating, which takes an input and an output message and is thus synchronous:
<portType name="BookRatingPT">
  <operation name="BookRating">
    <input message="tns:BookRatingRequestMessage" /> 
    <output message="tns:BookRatingResponseMessage" /> 
  </operation>
</portType>
The signatures of both messages are as follows:
<message name="BookRatingRequestMessage">
  <part name="book" type="tns:BookDscType" /> 
</message>

<message name="BookRatingResponseMessage">
  <part name="rating" type="xs:int" />
</message>
The input parameter to the operation is of type BookDscType. To map the BookDscType to Java, use the corresponding XML façade, which you generated earlier using the schemac tool. The return type of the operation is theBookRatingResponseMessage message, which is of type xs:intxs:int type maps to java.lang.Integer. (It could also map toint or java.math.BigInteger, but you are using java.lang.Integer here.)
You are now ready to write the Java equivalent class for the book rating Web service. Call the new Java classBookRatingJava, which will have a single method called getBookRating. The method body will be oversimplified—you will print a notification to the server console and return the rating of 4. (In a real-world example, you could calculate the rating based on data in the database, for example.) The code is as follows (note how you can access the book title and ISSN usinggetTitle() and getISSN() methods respectively):
package com.oracle.rating; 
import com.oracle.service.bookrating.*;

public class BookRatingJava {

 public Integer getBookRating (BookDscType book) {
  System.out.println("Book rating for "+book.getTitle()+" ("+book.getISSN()+"): 4.");
  return new Integer(4);
 }

}
The console output is added here to verify that the process really calls the Java class and not the Web service.

Defining WSIF bindings in WSDL

To "persuade" the BPEL process to use the Java class instead of the Web service, you have to define the WSIF bindings to the Java class. This is done in the book rating WSDL, where you add the binding section.
Each WSIF binding consists of two parts. First, you have to define the actual binding, where you specify:
  • Type of binding used (Java class, EJB, JCA, and so on).
  • Mapping of types, where you specify the mapping of XML types to the destination types (for Java resources, these are Java types). You have to define the mapping for all complex types; simple types are mapped automatically based on the table presented earlier.
  • Mapping of operations, where you have to specify for each WSDL operation (defined under the <portType> tag) the corresponding operation in the destination resource (for example, the name of the method of a Java class).
Second, you have to specify the service you will be using. Here you specify the exact name of the resource. If it is a Java class, specify its full name (including the package name).
In a real-world scenario, you may have a resource, such as a Java class or an EJB, for which a WSDL will not exist. Then you have to go through the following steps:
  1. Define the Java-to-XML bindings, where you select how to map input parameters and return value to XML. You can use XML façades and simplify the work using the schemac tool with the -Rswitch, which will generate an XML Schema based on a Java class.
  2. Define the signature for each operation and the corresponding input and output messages.
  3. Add the WSIF binding.
  4. Add the <partnerLinkType> declaration in order to use the WSDL from the BPEL
  5. process.
Particularly in the first two steps you can use a tool or a wizard for automatic conversion of resources to Web services. Such tools are available for most environments. Of course, you do not actually convert the resource to a Web service, but can make use of the generated WSDL (with additional modifications).

WSIF binding for Java class

Let's now define the WSIF binding for BookRatingJava class. Start by defining the two namespaces used by WSIF providers in the root element of the WSDL document, the <definitions> tag. The format namespace is used to define the type mappings and the javanamespace to define the operation mappings and the full name of the Java class:
<?xml version="1.0" encoding="utf-8" ?> 
<definitions xmlns:xs="http://www.w3.org/2001/XMLSchema" 
             xmlns:tns="http://oracle.com/service/bookrating/" 
             targetNamespace="http://oracle.com/service/bookrating/" 
             xmlns="http://schemas.xmlsoap.org/wsdl/"
             xmlns:plnk="http://schemas.xmlsoap.org/ws/2003/05/partner-link/" 
             xmlns:format="http://schemas.xmlsoap.org/wsdl/formatbinding/"
             xmlns:java="http://schemas.xmlsoap.org/wsdl/java/" >
...
Next, add the binding section. This section is usually located after port type declarations and before partner link types. Here you define a Java binding for the BookRatingPT port type:
  1. Define the type mapping from XML to Java. The input parameter XML type BookDscType is mapped to thecom.oracle.service.bookrating.BookDscType Java class. Please note that you do not have to provide the mapping for output XML xs:int type, because it maps automatically to java.lang.Integer.
  2. Also define that the WSDL operation BookRating is mapped to the Java method getBookRating(). Please note that the name of the WSDL operation and the method name of the Java class do not need to be equal, but the types of input and return parameters do:
    ...
          <binding name="JavaBinding" type="tns:BookRatingPT">
        
            <java:binding/>
        
            <format:typeMapping encoding="Java" style="Java">
              <format:typeMap typeName="tns:BookDscType" 
                      formatType="com.oracle.service.bookrating.BookDscType" />
            </format:typeMapping>
        
            <operation name="BookRating">
              <java:operation methodName="getBookRating"/>
              <input/>
              <output/>
            </operation>     
        
          </binding>
        ...
    
Next, specify the service used. Define that the service is provided by the Java class. The book rating service will use thecom.oracle.rating.BookRatingJava Java class:
...
  <service name="BookRating">

    <port name="JavaPort" binding="tns:JavaBinding">
      <java:address className="com.oracle.rating.BookRatingJava"/>
    </port>

  </service>
The rest of the book rating WSDL (including partner link types) has not changed.

Testing the example

You are almost ready to test the example and verify that the BPEL process will use the Java class instead of the original Web service. Remember, you have modified the WSDL only; you have not made any changes to the BPEL process code.
You will use the original BPEL code and the same partner link and invoke the BookRatingJava Java class with the usual<invoke> activity used for invoking the Web service. Recall the BPEL code for the invocation of the book rating service:
<!-- Synchronously invoke the Book Rating Web Service -->
          <scope name="BookRatingInvoke">
          
            <faultHandlers>

              <catchAll>
                <!-- If book rating is not available assign 0 -->
                <assign>
                  <copy>
                    <from expression="number(0)"/>
                    <to variable="BookRatingResponse" part="rating"/>
                  </copy>
                </assign>          
              </catchAll>
              
            </faultHandlers>
            
            <invoke partnerLink="BookRating" 
                  portType="bkr:BookRatingPT" 
                  operation="BookRating"
                  inputVariable="BookRatingRequest" 
                  outputVariable="BookRatingResponse" />
          
          </scope>

Before we can test the example, we have to do a few "bookkeeping" activities. First, we have to ensure that the BPEL process will use the modified WSDL. To achieve this, modify the bpel.xml file and specify that the BookRating.wsdl file should be taken from the current directory and not from the Web service itself:
<?xml version="1.0" encoding="UTF-8"?>
<BPELSuitcase>
  <BPELProcess src="BuyBook.bpel" id="BuyBookJavaBinding">
    <partnerLinkBindings>
      <partnerLinkBinding name="Client">
        <property name="wsdlLocation">
           BuyBook.wsdl
        </property>
      </partnerLinkBinding>
      <partnerLinkBinding name="BookRating">
        <property name="wsdlLocation">
            BookRating.wsdl
        </property>
      </partnerLinkBinding>
      <partnerLinkBinding name="BookStore1">
        <property name="wsdlLocation">
            http://localhost:9700/orabpel/default/BookStore1/BookStore1?wsdl
        </property>
      </partnerLinkBinding>
      <partnerLinkBinding name="BookStore2">
        <property name="wsdlLocation">
            http://localhost:9700/orabpel/default/BookStore2/BookStore2?wsdl
        </property>
      </partnerLinkBinding>
    </partnerLinkBindings>
  </BPELProcess>
</BPELSuitcase>
We then generate the XML façade using the schemac utility and compile the BookRatingJava class. You have to deploy the XML façade and the Java class to the C:\OraBPELPM_1\integration\bpelpm\orabpel\system\classes directory, where the BPEL server can locate and use them. The easiest way is to modify the build.xml, which should invoke the schemac compiler, the javac compiler, and the bpelc compiler:
<?xml version="1.0"?>
<project name="BuyBookJavaBinding" default="all" basedir=".">
  <property name="deploy" value="default"/>
  <property name="rev" value="1.0"/>

    <target name="CompileJava">
      <schemac input="${basedir}/BookRating.wsdl"
               out="${home}/system/classes"/>
      <javac srcdir="${basedir}/src" destdir="${home}/system/classes"/>
    </target> 
       
    <target name="main">
      <bpelc home="${home}" rev="${rev}" deploy="${deploy}"/>        
    </target>    

    <target name="all" depends="CompileJava, main"/>

</project>
After starting the obant utility, you should get the following output:
Z:\WSIF\2_JavaBindingClass>obant

Z:\WSIF\2_JavaBindingClass>SETLOCAL
Buildfile: build.xml

CompileJava:
  [schemac] schemac> parsing schema file 'Z:\WSIF\2_JavaBindingClass/BookRating.
wsdl' ...
  [schemac] schemac> Loaded schemas from wsdl located at Z:\WSIF\2_JavaBindingCl
ass/BookRating.wsdl
  [schemac] schemac> generating XML business document ...
  [schemac] schemac> compiling XML business documents ...
    [javac] Compiling 1 source file to C:\OraBPELPM_1\integration\orabpelsystem\classes

main:
    [bpelc] validating "Z:\WSIF\2_JavaBindingClass\BuyBook.bpel" ...
    [bpelc] BPEL suitcase deployed to: C:\OraBPELPM_1\integration\orabpel\domain
s\default\deploy

all:

BUILD SUCCESSFUL
Total time: 17 seconds

Z:\WSIF\2_JavaBindingClass>ENDLOCAL

Z:\WSIF\2_JavaBindingClass>
Next, use the BPEL Console to start the process. In the visual flow window, you can observe the execution of the process. Note that the <invoke> activity has been used in BPEL and that the book rating is 4, in contrast to the original book rating Web services, which returned 5, as shown in the figure below.
To be absolutely sure that the BPEL Process Manager has invoked the Java class, the BPEL Process Manager console window will show the following output:
05/10/19 19:35:36 Book rating for Business Process Execution Language 
(1-904811-18-3): 4.

0 comments:

Post a Comment

 
Blogger Profile