Oracle® BPEL Process Manager Developer's Guide
10g Release 2 (10.1.2) B14448-02 |
|
Previous |
Next |
This chapter describes how to manipulate XML data in BPEL, including the large role that XPath expressions play in manipulating XML data.
This chapter contains the following topics:
This chapter covers a variety of use cases for how you can manipulate XML data in BPEL. The use cases include topics such as how to work with variables, perform tasks such as mathematical calculations, and work with sequences and arrays. This chapter reviews the simplest to the most advanced methods for manipulating XML data in BPEL. The explanations are largely by example, and provide an introduction to the supporting specifications without repeating their details.
Most of the examples in this chapter assume that the WSDL file defining the associated message types is document-literal style rather than the RPC style. There is a difference in how XPath query strings are formed for RPC-style WSDL definitions. If you are working with a type defined in an RPC WSDL file, see "Differences Between Document-Style and RPC-Style WSDL Files".
See Also: The sample files located at
|
This section covers the following topics:
In a BPEL process, every piece of data is in XML format. This includes the messages passed to and from the BPEL process, the messages exchanged with external services, and local variables used by the flow. You define the types for these messages and variables with the XML schema, usually in the WSDL file for the flow or in the WSDL files for the services it invokes. Therefore, all variables in BPEL are XML data, and any BPEL process uses much of its code to manipulate these XML variables. This typically includes performing data transformation between representations required for different services, and local manipulation of data (for example, to combine the results from several service invocations).
The starting point for data manipulation in BPEL is the assign activity, which builds on the XPath standard. XPath queries, expressions, and functions play a large part in this type of manipulation. In addition, more advanced methods are available that involve using XQuery, XSLT, or Java, usually to do more complex data transformation or manipulation.
This section provides a general overview of how to manipulate XML data in BPEL. It summarizes the key building blocks used in various combinations and provides examples. The remaining sections in this chapter discuss and illustrate how to apply these building blocks to perform specific tasks.You use the assign
activity to copy data from one XML variable to another, or to calculate the value of an expression and store it in a variable. A copy element within the activity specifies the source and target of the assignment (what to copy from and to), which must be of compatible types. The formal syntax as shown in the Business Process Execution Language for Web Services Specification is as follows:
<assign standard-attributes> standard-elements <copy>+ from-spec to-spec </copy> </assign>
This syntax is described in detail in that specification. The from-spec
and to-spec
typically specify a variable or variable part, as in:
<assign> <copy> <from variable="c1" part="address"/ <to variable="c3"/> </copy> </assign>
Rather than repeating all syntax details, this chapter shows and describes excerpts taken primarily from sample projects provided in the C:\orabpel\samples\references
directory for Eclipse BPEL Designer and Oracle_Home
\integration\orabpel\samples\references
directory for JDeveloper BPEL Designer.
Note: If you used the tutorials with either BPEL designer (JDeveloper BPEL Designer or Eclipse BPEL Designer) to add an assign activity to the process, you supplied details about the activity in a Copy Rule window that included a From section and a To section. This reflects the underlying BPEL source code syntax shown previously in this section. |
XPath standards play a key role in the assign activity. Brief examples are shown here as an introduction; examples with more context and explanation are provided in the sections that follow.
XPath queries: An XPath query selects a field within a source or target variable part. The from
or to
clause can include a query attribute whose value is an XPath query string. For example:
<from variable="input" part="payload" query="/p:CreditFlowRequest/p:ssn">
For XPath version 1.0, the value of the query attribute must be an absolute location path that selects exactly one node. You can find further details about the query attribute and XPath standards syntax in the Business Process Execution Language for Web Services Specification (section 14.3) and the XML Path Language (XPath) Specification, respectively.
XPath expressions: You use an XPath expression (specified in an expression
attribute in the from
clause) to indicate a value to be stored in a variable. For example:
<from expression="100"/>
The expression can be any general expression—that is, an XPath expression that evaluates to any XPath value type. For more information about XPath expressions, see section 9.1.4 of the XML Path Language (XPath) Specification.
Within XPath expressions, you can call the following types of functions:
Core XPath functions: XPath supports a large number of built-in functions, including functions for string manipulation (such as concat
), numeric functions (like sum
), and others.
<from expression="concat('string one', 'string two')"/>
For a complete list of the functions built into XPath standards, see section 4 of the XML Path Language (XPath) Specification.
BPEL XPath extension functions: BPEL adds several extension functions to the core XPath core functions, enabling XPath expressions to access information from a process. The extensions are defined in the standard BPEL namespace http://schemas.xmlsoap.org/ws/2003/03/business-process/
and indicated by the prefix bpws
:
<from expression= Òbpws:getVariableData('input', 'payload', '/p:value') + 1"/>
For more information, see sections 9.1 and 14.1 of the Business Process Execution Language for Web Services Specification.
Oracle BPEL Extension XPath functions: Oracle provides some additional XPath functions that use the capabilities built into BPEL and XPath standards for adding new functions.
These functions are defined in the namespace http://schemas.oracle.com/xpath/extension
and indicated by the prefix ora:
. If you have Oracle BPEL Process Manager installed locally, Oracle BPEL Console lists and describes all available XPath functions through the following URL:
See Also: The XPath extension documentation available at either of the following locations:
|
Custom functions: You can also create custom XPath functions. If you do, you must register them in the BPEL process deployment descriptor or in the following XML files:
C:\orabpel\domains\default\config\xpath-functions.xml
file (for Eclipse BPEL Designer)
Oracle_Home
\integration\orabpel\domains\default\config\xpath-functions.xml
file (for JDeveloper BPEL Designer)
Then, package the source implementing them into a BPEL suitcase or Oracle BPEL Process Manager startup environment. For more information about writing custom XPath functions, refer to: http://www.oracle.com/technology/bpel
Sophisticated data manipulation can be difficult to perform with the BPEL assign activity and the core XPath functions. However, you can perform complex data manipulation and transformation by using XSLT or Java, or as a Web service. For more information on calling Java code from within BPEL, see the tutorial under the BPEL Tutorials link at http://www.oracle.com/technology/bpel
. For XSLT, Oracle BPEL Process Manager includes XPath functions that execute these transformations.
See Also: The following XPath and XQuery transformation code examples:
|
The following sections show related definitions in the BPEL and WSDL files that help explain the examples.
It is often useful to assign literal XML to a variable in BPEL, for example, to initialize a variable before copying dynamic data into a specific field within the XML data content for the variable. This is also useful for testing purposes when you want to hard code XML data values into the process.
This example assigns a literal result
element to the payload
part of the output
variable:
<assign> <!-- copy from literal xml to the variable --> <copy> <from> <result xmlns="http://samples.otn.com"> <name/> <symbol/> <price>12.3</price> <quantity>0</quantity> <approved/> <message/> </result> </from> <to variable="output" part="payload"/> </copy> </assign>
See Also: The following samples:
|
When you copy between variables, you copy directly from one variable (or part) to another variable of a compatible type, without needing to specify a particular field within either variable. In other words, there is no need to specify an XPath query.The following example performs two assignments, first copying between two variables of the same type and then copying a variable part to another variable with the same type as that part.
<assign> <copy> <from variable="c1"/> <to variable="c2"/> </copy> <copy> <from variable="c1" part = "address"/> <to variable="c3"/> </copy> </assign>
The BPEL file defines the variables as follows:
<variable name="c1" messageType="x:person"/> <variable name="c2" messageType="x:person"/> <variable name="c3" element="x:address"/>
The WSDL file defines the person message type as follows:
<message name="person" xmlns:x="http://tempuri.org/bpws/example"> <part name="full-name" type="xsd:string"/> <part name="address" element="x:address"/> </message>
See Also: Section 9.3.2 of the Business Process Execution Language for Web Services Specification for this code example |
Given the types of definitions present in most WSDL files, you must go down to the level of copying from or to a field within part of a complex type variable. To do this, you specify an XPath query in the from
or to
clause of the assign activity.
This example copies the ssn
field from the CreditFlow
process's input message into the ssn
field of the credit rating service's input message.
<assign> <copy> <from variable="input" part="payload" query="/tns:CreditFlowRequest/tns:ssn"/> <to variable="crInput" part="payload" query="/tns:ssn"/> </copy> </assign>
The BPEL file defines the variables involved in this assignment as follows:
<variable name="input" messageType="tns:CreditFlowRequestMessage"/> <variable name="crInput" messageType="services:CreditRatingServiceRequestMessage"/>
The crInput
variable is used as an input message to a credit rating service. Its message type, CreditFlowRequestMessage
, is defined in CreditFlowService.wsdl
as follows:
<message name="CreditFlowRequestMessage"> <part name="payload" element="tns:CreditFlowRequest"/> </message>
CreditFlowRequest
is defined with a field named ssn
. The message type CreditRatingServiceRequestMessage
is defined in CreditRatingService.wsdl
as follows:
<message name="CreditRatingServiceRequestMessage"> <part name="payload" element="tns:ssn"/> </message>
See Also: The following samples:
|
You can assign numeric values in XPath expressions. The following example shows how to assign an XPath expression with the integer value 100
.
<assign> <!-- copy from integer expression to the variable --> <copy> <from expression="100"/> <to variable="output" part="payload" query="/p:result/p:quantity"/> </copy> </assign>
See Also: The following samples:
|
You can use simple mathematical expressions like the one in the following example, which increments a numeric value.
In this example, the BPEL XPath function getVariableData
retrieves the value being incremented. The arguments to getVariableData
are equivalent to the variable, part, and query attributes of the from
clause (including the last two arguments, which are optional).
<assign> <copy> <from expression="bpws:getVariableData('input', 'payload', '/p:value') + 1"/> <to variable="output" part="payload" query="/p:result"/> </copy> </assign>
See Also: The following samples:
|
This example copies an expression evaluating to the string literal 'GE'
to the symbol field within the indicated variable part. (Note the use of the double and single quotes.)
<assign> <!-- copy from string expression to the variable --> <copy> <from expression="'GE'"/> <to variable="output" part="payload" query="/p:result/p:symbol"/> </copy> </assign>
See Also: The following samples:
|
Rather than copy the value of one string variable (or variable part or field) to another, you first can perform string manipulation, such as concatenating several strings together. In Chapter 3, "Building a Simple BPEL Process", you concatenated the string 'Hello '
with a name supplied in an input message. A similar example from the assign reference sample is shown in the following syntax. The concatenation is accomplished with the core XPath function named concat
; in addition, the variable value involved in the concatenation is retrieved with the BPEL XPath function getVariableData
.
In this example, getVariableData
fetches the value of the name field from the input
variable's payload
part. The string literal 'Hello '
is then concatenated to the beginning of this value.
<assign> <!-- copy from XPath expression to the variable --> <copy> <from expression="concat('Hello ', bpws:getVariableData('input', 'payload', '/p:name'))"/> <to variable="output" part="payload" query="/p:result/p:message"/> </copy> </assign>
Other string manipulation functions available in XPath are listed in section 4.2 of the XML Path Language (XPath) Specification.
See Also: The following samples:
|
In this example of assigning Boolean values, the XPath expression in the from
clause is a call to XPath's Boolean function true
, and the specified approved field is set to true
. The function false
is also available.
<assign> <!-- copy from boolean expression function to the variable --> <copy> <from expression="true()"/> <to variable="output" part="payload" query="/result/approved"/> </copy> </assign>
See Also: The following samples:
|
You can assign the current value of a date or time field by using the BPEL XPath function getCurrentDate
, getCurrentTime
, or getCurrentDateTime
, respectively. In addition, if you have a date-time value in the standard XSD format, you can convert it to characters more suitable for output by calling the BPEL XPath function formatDate
.For related information, see section 9.1.2 of the Business Process Execution Language for Web Services Specification.
<!-- execute the XPath extension function getCurrentDate() --> <assign> <copy> <from expression="ora:getCurrentDate()"/> <to variable="output" part="payload" query="/invoice/invoiceDate"/> </copy> </assign>
In the next example, the formatDate
function converts the date-time value provided in XSD format to the string 'Jun 10, 2005'
(and assigns it to the string field formattedDate
).
<!-- execute the XPath extension function formatDate() --> <assign> <copy> <from expression="ora:formatDate('2005-06-10T15:56:00', 'MMM dd, yyyy')"/> <to variable="output" part="payload" query="/invoice/formattedDate"/> </copy> </assign>
See Also: The following samples:
|
You may want to copy to or from something defined as an XML attribute. An at sign (@
) in XPath query syntax refers to an attribute instead of a child element.
The following code example fetches and copies the custId
attribute from this XML data:
<invalidLoanApplication xmlns="http://samples.otn.com"> <application xmlns = "http://samples.otn.com/XPath/autoloan"> <customer custId = "111" > <name> Mike Olive </name> ... </customer> ... </application> </invalidLoanApplication>
The following example selects the custId
attribute of the customer field and assigns it to the variable custId
:
<assign> <!-- get the custId attribute and assign to variable custId --> <copy> <from variable="input" part="payload" query="/tns:invalidLoanApplication/autoloan:application /autoloan:customer/@custId"/> <to variable="custId"/> </copy> </assign>
The namespace prefixes in this example are optional and not integral to the example.The WSDL file defines a customer to have a type in which custId
is defined as an attribute, as follows:
<complexType name="CustomerProfileType"> <sequence> <element name="name" type="string"/> ... </sequence> <attribute name="custId" type="string"/> </complexType>
See Also: The following samples:
|
Data sequences are one of the most basic data models used in XML. However, manipulating them can be nontrivial. One of the most common data sequence patterns used in BPEL processes are arrays. Based on the XML schema, the way you can identify a data sequence definition is by its attribute maxOccurs
being set to a value of more than one or marked as unbounded. See the XML Schema Specification at http://www.w3.org/TR
for more information.
The examples in this section illustrate several basic ways of manipulating data sequences in BPEL. However, there are other associated requirements, such as performing looping or dynamic referencing of endpoints. For additional code samples and further information regarding real-world use cases for data sequence manipulation in BPEL, see http://www.oracle.com/technology/bpel
.Each of the following sections describes a particular requirement for data sequence manipulation. For a code example that describes all data sequences, see ArraySample.bpel
, which takes a data sequence as input and loops through it, adding together individual line items in each data sequence element into a total value.
See Also: TheArraySample.bpel sample file located at:
|
The following two examples illustrate how to use XPath functionality to select a data sequence element when the index of the element you want is known at design time. In these cases, it is the first element.
In the following example, addresses[1]
selects the first element of the addresses data sequence:
<assign> <!-- get the first address and assign to variable address --> <copy> <from variable="input" part="payload" query="/tns:invalidLoanApplication/autoloan:application /autoloan:customer/autoloan:addresses[1]"/> <to variable="address"/> </copy> </assign>
In this query, addresses[1]
is equivalent to addresses[position()=1]
, where position
is one of the core XPath functions (see sections 2.4 and 4.1 of the XML Path Language (XPath) Specification). The query in the next example calls the position function explicitly to select the first element of the addresses data sequence. It then selects that address's street
element (which the activity assigns to the variable street1
).
<assign> <!-- get the first address's street and assign to street1 --> <copy> <from variable="input" part="payload" query="/tns:invalidLoanApplication/autoloan:application /autoloan:customer/autoloan:addresses[position()=1] /autoloan:street"/> <to variable="street1"/> </copy> </assign>
If you review the definition of the input variable and its payload part in the WSDL file, you go several levels down before coming to the definition of the addresses field. There you see the maxOccurs="unbounded"
attribute. The two XPath indexing methods are functionally identical; you can use whichever method you prefer.
See Also: The following samples:
|
If you need to know the run-time size of a data sequence—that is, the number of nodes or data items in the sequence—you can get it by using the combination of the XPath built-in count()
function and the BPEL built-in getVariableData()
function.
This example calculates the number of elements in the item
sequence and assigns it to the integer variable lineItemSize
:
<assign> <copy> <from expression="count(bpws:getVariableData('outpoint', 'payload', '/p:invoice/p:lineItems/p:item')"/> <to variable="lineItemSize"/> </copy> </assign>
See Also: The following samples:
|
Often a dynamic value is needed to index into a data sequence—that is, you need to get the nth
node out of a sequence, where the value of n
is defined at run time. This section covers the following methods for dynamically indexing by applying a trailing XPath into expressions:
The dynamic indexing method shown here applies a trailing XPath to the result of bwps:getVariableData()
, instead of using an XPath as the last argument of bpws:getVariableData()
. The trailing XPath references to an integer-based index variable within the position predicate (that is, [...]
):
<variable name="idx" type="xsd:integer"/> ... <assign> <copy> <from expression="bpws:getVariableData('input','payload' )/p:line-item[bpws:getVariableData('idx')]/p:line-total" /> <to variable="lineTotalVar" /> </copy> </assign>
Assume at run time that the idx
integer variable holds 2
as its value. The preceding expression within the from
is equivalent to:
<from expression="bpws:getVariableData('input','payload' )/p:line-item[2]/p:line-total" />
There are some subtle XPath usage differences, when an XPath used trailing behind the bwps:getVariableData()
function is compared with the one used inside the function.Using the same example (where payload
is the message part of element "p:invoice"
), if the XPath is used within the getVariableData()
function, the root element name ("/p:invoice"
) must be specified at the beginning of the XPath.
For example:
bpws:getVariableData('input', 'payload','/p:invoice/p:line-item[2]/p:line-total')
If the XPath is used trailing behind the bwps:getVariableData()
function, the root element name does not need to be specified in the XPath.
For example:
bpws:getVariableData('input', 'payload')/p:line-item[2]/p:line-total
This is because the node returned by the getVariableData()
function is already the root element. Specifying the root element name again in the XPath is redundant and is standard XPath semantics.
The bpelx:append
extension under assign
enables BPEL processes to append new elements to an existing parent element:
<assign name="assign-3"> <copy> <from expression="bpws:getVariableData('idx')+1" /> <to variable="idx"/> </copy> <bpelx:append> <bpelx:from variable="partInfoResultVar" part="payload" /> <bpelx:to variable="output" part="payload" /> </bpelx:append> ... </assign>
The bpelx:append
logic in this example appends the payload element of the partInfoResultVar
variable as a child to the payload element of the output
variable. In others words, the payload element of output
variable is used as the parent element.
See Also: The following samples:
|
You can merge two sequences into a single data sequence. This pattern is common when the data sequences are in an array (that is, the sequence of data items of compatible types).The following two append
operations under assign
demonstrate how to merge data sequences:
<assign> <!-- initialize "mergedLineItems" variable to an empty element --> <copy> <from> <p:lineItems /> </from> <to variable="mergedLineItems" /> </copy> <bpelx:append> <bpelx:from variable="input" part="payload" query="/p:invoice/p:lineItems/p:lineitem" /> <bpelx:to variable="mergedLineItems" /> </bpelx:append> <bpelx:append> <bpelx:from variable="literalLineItems" query="/p:lineItems/p:lineitem" /> <bpelx:to variable="mergedLineItems" /> </bpelx:append> </assign>
See Also: TheArraySample.bpel sample file located at:
|
If you do not want to use the two-step process of creating an XPath query to dynamically index into a sequence, you can use the XPath function getElement
instead. This function takes a sequence and an index (which can be a dynamic value, such as a variable) and returns the appropriate sequence element.
<variable name="lineItemIndex" type="xsd:int"/> ... <!-- execute the XPath extension function getElement(arrayOfElements[], index) to fetch one element from an array of elements --> <assign> <copy> <from expression="ora:getElement('output', 'payload', '/invoice/lineItems/item', bpws:getVariableData('lineItemIndex'))"/> <to variable="myLineItem"/> </copy> </assign>
See Also: The following samples:
|
You can merge two sequences of compatible types into a single sequence. To do so, use the XPath function mergeChildNodes
.
<!-- execute the XPath extension function mergeChildNodes(e1, e2) and assign to a variable --> <assign> <copy> <from expression="ora:mergeChildNodes( bpws:getVariableData('input', 'payload', '/invoice/lineItems'), bpws:getVariableData('literalLineItems'))"/> <to variable="mergedLineItems"/> </copy> </assign>
See Also: The following samples:
|
The BPEL XPath function addChildNode
enables BPEL processes to append new elements to an existing sequence.
<!-- execute the XPath extension function addChildNode(Element e, Node childNode) --> <assign> <copy> <from expression="ora:addChildNode(bpws:getVariableData('output', 'payload', '/invoice/lineItems'), bpws:getVariableData('escapedLineItem'))"/> <to variable="output" part="payload" query="/invoice/lineItems"/> </copy> </assign>
See Also: The following samples:
|
Oracle BPEL Process Manager does not support SOAP-encoded arrays (soapenc:arrayType
).
Use one of the following workarounds:
Apache Axis supports document-literal style services. This means you can change the service to not use soapenc:arrayType
.
A wrapper can be placed around the service (also using Apache Axis) so that the BPEL process talks to the document literal wrapper service, which in turn calls the underlying service with soapenc:arrayType.
Call a service with soapenc:arrayType
from BPEL, but construct the XML message more manually in the BPEL code. This enables you to avoid changing or wrapping the service. However, each time you want to call that service from BPEL, you must take extra steps.
Sometimes a service is defined to return a string, but the content of the string is actually XML data. The problem is that, although BPEL provides support for manipulating XML data (using XPath queries, expressions, and so on), this functionality is not available if the variable or field is of type string. With Java, you use document object model (DOM) functions to convert the string to a structured XML object type. You can use the BPEL XPath function parseEscapedXML
to do the same thing. This function takes XML data, parses it through DOM, and returns structured XML data that can be assigned to a typed BPEL variable. For example:
<!-- execute the XPath extension functionparseEscapedXML('<item>') and assign to a variable --> <assign> <copy> <from expression="ora:parseEscapedXML( '<item xmlns="http://samples.otn.com" sku="006"> <description>sun ultra sparc VI server </description> <price>1000 </price> <quantity>2 </quantity> <lineTotal>2000 </lineTotal> </item>')"/> <to variable="escapedLineItem"/> </copy> </assign>
See Also: The following samples:
|
The examples shown up to this point have been for document-style WSDL files, in which a message is defined with an XML schema element, as in the following example:
<message name="LoanFlowRequestMessage"> <part name="payload" element="s1:loanApplication"/> </message>
This is in contrast to RPC-style WSDL files, in which the message is defined with an XML schema type
, as in:
<message name="LoanFlowRequestMessage"> <part name="payload" type="s1:LoanApplicationType"/> </message>
This affects the material in this chapter because there is a difference in how XPath queries are constructed for the two WSDL message styles. For an RPC-style message, the top-level element (and therefore the first node in an XPath query string) is the part name (payload
in the previous example). In document-style, the top-level node is the element name (for example, loanApplication
).The following example shows what an XPath query string looks like if the LoanServices
used in BPEL demo applications (such as LoanFlow
) were RPC style.
RPC-Style WSDL File
<message name="LoanServiceResultMessage"> <part name="payload" type="s1:LoanOfferType"/> </message> <complexType name="LoanOfferType"> <sequence> <element name="providerName" type="string"/> <element name="selected" type="boolean"/> <element name="approved" type="boolean"/> <element name="APR" type="double"/> </sequence> </complexType>
RPC-Style BPEL File
<variable name="output" messageType="tns:LoanServiceResultMessage"/> ... <assign> <copy> <from expression="9.9"/> <to variable="output" part="payload" query="/payload/APR"/> </copy> </assign>
See Also: The following samples:
|
If you use RPC-style messaging, you must enclose the input XML message with an operation name (in the following example, process
):
<process xmlns="http://xmlns.oracle.com/MessageParts_referingToComplexTypes"> <payload xmlns="" xmlns:client="http://xmlns.oracle.com/MessageParts_referingToComplexTypes" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="client:TestInput"> <input xmlns="http://xmlns.oracle.com/MessagePartsreferingToComplexTypes">yoo</input > <input1 xmlns="http://xmlns.oracle.com/MessageParts_referingToComplexTypes">boo</input 1> <input2 xmlns="http://xmlns.oracle.com/MessageParts_referingToComplexTypes">woo</input 2> <input3 xmlns="http://xmlns.oracle.com/MessageParts_referingToComplexTypes">foo</input 3> <input4 xmlns="http://xmlns.oracle.com/MessageParts_referingToComplexTypes">goo</input 4> </payload> </process>
If you want to verify the correctness of the XML file, set the validateXML property to true in the Manage BPEL Domain window of Oracle BPEL Console.
There are two supported methods for transferring opaque data in a SOAP call:
Embedding data
This method embeds opaque data as element or attribute content. XML supports opaque data as content through either base64 or hexadecimal text encoding. XML schema's two binary data types, xs:base64Binary
and xs:hexBinary
, are used with this method. Since the opaque data is converted to a basic XML schema type, it can be manipulated like other XML data in a BPEL process in terms of being assigned, sent, and received in standard BPEL activities.
Data encoded in base64 format expands by a factor of 1.33 times the original size. Hexadecimal encoded data expands by a factor of 2 times. This is assuming an underlying UTF-8
text encoding is used in both cases; if the underlying text encoding used is UTF-16
, these numbers double. To achieve better performance at both the SOAP layer and BPEL execution layer, keep the original data as it is when transferring it through SOAP. The second method described below, SOAP with an attachment, provides a technique for doing this.
SOAP with an attachment can be achieved through use of HTTP with the Multipurpose Internet Mail Extensions (MIME) or Direct Internet Message Encapsulation (DIME) protocols. The SOAP envelope and opaque data are wrapped in MIME or DIME sections.
The following example shows a message using SOAP with an attachment through use of the MIME protocol. The attachment is passed by reference through use of an identifying key, instead of being copied. Note that with MIME, the XML part and the binary part must appear in separate parts. The XML part contains a reference to the binary attachment part. This differs from DIME, which does not have this restriction.
MIME-Version: 1.0 Content-Type: Multipart/Related; boundary=MIME_boundary; type=text/xml; start="<mymessage.xml@collaxa.com>" Content-Description: A SOAP Envelope with pdf --MIME_boundary Content-Type: text/xml; charset=UTF-8 Content-Transfer-Encoding: 8bit Content-ID: <mymessage.xml@collaxa.com> <s:Envelope xmlns:s='http://www.w3.org/2002/12/soap-envelope' > <s:Body> <m:applyLoan xmlns:m='http://samples.Collaxa.com/MIMEService' > <customerName>John Doe</customerName> <pdf data="1234567890" /> </m:applyLoan> </s:Body> </s:Envelope> --MIME_boundary Content-Type: application/pdf Content-Transfer-Encoding: binary Content-Location: 1234567890 fd a5 8a 29 aa 46 1b 24 --MIME_boundary
The binary data is still typed as base64binary
or hexBinary
. However, the data remains in binary format through transporting and processing. There is no overhead added with encoding and decoding.
This section provides a use case and describes design implementation issues. You cannot currently model a SOAP message with an attachment through either BPEL designer (JDeveloper BPEL Designer or Eclipse BPEL Designer). You must manually edit the necessary BPEL and WSDL files.
In this use case:
The BPEL process acts as a service to receive and reply to a SOAP message with an attachment.
The BPEL process acts as a client to send and receive a response to a SOAP message with an attachment.
Binary data is assigned to another variable.
Binary data is read from a URL.
Binary data is saved to a local file.
Two BPEL processes are constructed:
MIMEService is essentially an echo service of a SOAP message with an attachment. This process does the following:
Receives a SOAP message with an attachment and saves the opaque data to a local file.
Assigns the opaque data to an output variable.
Uses the output variable to reply to a SOAP message with an attachment to the invoker.
MIMERequester is the client of the first service. This process does the following:
Reads the binary data from a URL and assigns it to a variable.
Uses the variable as input to perform an invoke on the first process.
Receives a SOAP message with an attachment.
The following WSDL file defines MIMEService. This file uses the WSDL binding MIME extension to define the SOAP message with an attachment.
<?xml version="1.0" encoding="UTF-8"?> <definitions . . . . . . <types> . . . . . . <schema xmlns="http://www.w3.org/2001/XMLSchema"> <import namespace="http://schemas.xmlsoap.org/ws/2003/03/addressing" schemaLocation="http://gmi-pc:9700/orabpel/xmllib/ws-addressing.xsd" /> </schema> </types> <message name="ReplyToHeader"> <part name="ReplyTo" element="wsa:ReplyTo" /> </message> <message name="MessageIDHeader"> <part name="MessageID" element="wsa:MessageID" /> </message> <message name="RelatesToHeader"> <part name="RelatesTo" element="wsa:RelatesTo" /> </message> <message name="MIMEServiceRequestMessage"> <part name="payload" type="xsd:int"/> <part name="bin" type="xsd:anyType"/> </message> <message name="MIMEServiceResponseMessage"> <part name="payload" type="xsd:int"/> <part name="bin" type="xsd:anyType"/> </message> <portType name="MIMEService"> <operation name="initiate"> <input message="tns:MIMEServiceRequestMessage"/> </operation> <operation name="process"> <input message="tns:MIMEServiceRequestMessage"/> <output message="tns:MIMEServiceResponseMessage"/> </operation> </portType> <portType name="MIMEServiceCallback"> <operation name="onResult"> <input message="tns:MIMEServiceResponseMessage"/> </operation> </portType> <binding name="MIMEServiceBinding" type="tns:MIMEService"> <soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/> <operation name="initiate"> <soap:operation style="rpc" soapAction="initiate"/> <input> <mime:multipartRelated> <mime:part> <soap:header message="tns:MessageIDHeader" part="MessageID" use="literal"/> <soap:header message="tns:ReplyToHeader" part="ReplyTo" use="literal"/> <soap:body parts="payload" use="literal"/> </mime:part> <mime:part> <mime:content part="bin" type="binary"/> </mime:part> </mime:multipartRelated> </input> </operation> <operation name="process"> <soap:operation style="rpc" soapAction="process"/> <input> <mime:multipartRelated> <mime:part> <soap:header message="tns:RelatesToHeader" part="RelatesTo" use="literal"/> <soap:body parts="payload" use="literal"/> </mime:part> <mime:part> <mime:content part="bin" type="binary"/> </mime:part> </mime:multipartRelated> </input> <output> <mime:multipartRelated> <mime:part> <soap:body parts="payload" use="literal"/> </mime:part> <mime:part> <mime:content part="bin" type="binary"/> </mime:part> </mime:multipartRelated> </output> </operation> </binding> <binding name="MIMEServiceCallbackBinding" type="tns:MIMEServiceCallback"> <soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/> <operation name="onResult"> <soap:operation style="rpc" soapAction="onResult"/> <input> <mime:multipartRelated> <mime:part> <soap:header message="tns:RelatesToHeader" part="RelatesTo" use="literal"/> <soap:body use="literal" /> </mime:part> <mime:part> <mime:content part="bin" type="binary"/> </mime:part> </mime:multipartRelated> </input> </operation> </binding> <plnk:partnerLinkType name="MIMEService"> <plnk:role name="MIMEServiceProvider"> <plnk:portType name="tns:MIMEService"/> </plnk:role> <plnk:role name="MIMEServiceRequester"> <plnk:portType name="tns:MIMEServiceCallback"/> </plnk:role> </plnk:partnerLinkType> </definitions>
In this WSDL, the schema type of the binary data is xsd:anyType
. This is because xsd:binary
was deprecated from XSD version 2000 to version 2001. (Web Services Description Language (WSDL) Specification 1.1 has an example using xsd:binary
. This has become an outstanding issue.)
To ensure that payload validation is successful, use xsd:anyType with either MIME or DIME.
The MIMERequester.bpel
file shows how to use binary data in BPEL:
<process . . . . . . . . . <sequence> <!-- receive input from requestor --> <receive name="receiveInput" partnerLink="client" portType="tns:MIMERequester" operation="initiate" variable="input" createInstance="yes"/> <!-- initialize the input of MIMEService --> <assign> <copy> <from variable="input" part="payload" query="/tns:value"/> <to variable="request" part="payload" query="/payload"/> </copy> <copy> <from expression="ora:readBinaryFromFile('request.bin')"/> <to variable="request" part="bin"/> </copy> </assign> <invoke name="invoke" partnerLink="MIMEService" portType="services:MIMEService" operation="initiate" inputVariable="request"/> <receive name="receive" partnerLink="MIMEService" portType="services:MIMEServiceCallback" operation="onResult" variable="response"/> <assign> <copy> <from variable="response" part="payload" query="/payload"/> <to variable="request" part="payload" query="/payload"/> </copy> <copy> <from expression="ora:writeBinaryToFile('response', 'bin', 'C:/Temp/response.bin')"/> <to variable="response" part="bin"/> </copy> <copy> <from expression="ora:readBinaryFromFile('request2.bin')"/> <to variable="request" part="bin"/> </copy> </assign> <invoke name="invoke" partnerLink="MIMEService" portType="services:MIMEService" operation="process" inputVariable="request" outputVariable="response"/> <assign> <copy> <from variable="response" part="payload" query="/payload"/> <to variable="output" part="payload" query="/tns:result"/> </copy> <copy> <from expression="ora:writeBinaryToFile('response', 'bin', 'C:/Temp/response2.bin')"/> <to variable="response" part="bin"/> </copy> </assign> <!-- respond output to requestor --> <invoke name="replyOutput" partnerLink="client" portType="tns:MIMERequesterCallback" operation="onResult" inputVariable="output"/> </sequence> </process>
In this example, XPath extension function ora:readBinaryFromFile( )
reads the binary file and ora:writeBinaryToFile( )
writes the binary content to a file.
The binary data can be assigned to another variable like a normal XML document by using the standard BPEL assign activity. The BPEL assign activity is extended here to accommodate the binary data.
MIMEService can be accessed from a Java client. There are two access options:
Java API for XML-Based RPC (JAX-RPC)
SOAP with Attachments API Java (SAAJ)
Example 4-1 uses Axis' implementation of SAAJ to invoke MIMEService. This example is used to unit test the interoperability of the created service. The sample request sent by this example is shown in Example 4-2.
Example 4-1 SAAJ Example
public boolean initiateUsingSAAJ(String filename) throws Exception { String endPointURLString = "http://localhost:" +opts.getPort() + "/orabpel/default/MIMEService/1.0"; SOAPConnectionFactory soapConnectionFactory = javax.xml.soap.SOAPConnectionFactory.newInstance(); SOAPConnection soapConnection = soapConnectionFactory.createConnection(); MessageFactory messageFactory = MessageFactory.newInstance(); SOAPMessage soapMessage = messageFactory.createMessage(); MimeHeaders hd = soapMessage.getMimeHeaders(); hd.addHeader("SOAPAction", "initiate"); SOAPPart soapPart = soapMessage.getSOAPPart(); SOAPEnvelope requestEnvelope = soapPart.getEnvelope(); SOAPBody body = requestEnvelope.getBody(); SOAPBodyElement operation = body.addBodyElement (requestEnvelope.createName("initiate")); Vector dataHandlersToAdd = new Vector(); dataHandlersToAdd.add(new DataHandler(new FileDataSource(new File(filename)))); javax.xml.soap.SOAPElement element1 = operation.addChildElement(requestEnvelope.createName("payload")); element1.addTextNode("1"); if (dataHandlersToAdd != null) { ListIterator dataHandlerIterator = dataHandlersToAdd.listIterator(); while (dataHandlerIterator.hasNext()) { DataHandler dataHandler = (DataHandler) dataHandlerIterator.next(); javax.xml.soap.SOAPElement element = operation.addChildElement(requestEnvelope.createName("bin")); javax.xml.soap.AttachmentPart attachment = soapMessage.createAttachmentPart(dataHandler); soapMessage.addAttachmentPart(attachment); element.addAttribute(requestEnvelope.createName ("href"), "cid:" + attachment.getContentId()); } } javax.xml.soap.SOAPMessage returnedSOAPMessage = soapConnection.call(soapMessage, endPointURLString); if (returnedSOAPMessage == null) return true; Iterator iterator = returnedSOAPMessage.getAttachments(); if (!iterator.hasNext()) { //The wrong type of object that what was expected. System.out.println("Received problem response from server"); throw new AxisFault("", "Received problem response from server", null, null); } //Still here, so far so good. //Now lets brute force compare the source attachment // to the one we received. DataHandler rdh = (DataHandler) ((AttachmentPart)iterator.next()).getDataHandler(); //From here we'll just treat the data resource as file. String receivedfileName = rdh.getName();//Get the filename. if (receivedfileName == null) { System.err.println("Could not get the file name."); throw new AxisFault("", "Could not get the file name.", null, null); } System.out.println("Going to compare the files.."); boolean retv = compareFiles(filename, receivedfileName); java.io.File receivedFile = new java.io.File(receivedfileName); receivedFile.delete(); return retv; } }
Example 4-2 shows the sample request sent by the SAAJ program:
Example 4-2 Sample Request
POST /orabpel/default/MIMEService/1.0 HTTP/1.0Content-Type: multipart/related; type="text/xml"; start="<F090DFD56421FD84AAF98C386AD50A44>"; boundary="----=_ Part_0_27211574.1133404205718"Accept: application/soap+xml, application/dime, multipart/related, text/*User-Agent: Axis/1.2.1Host: gmi-pc:1234Cache-Control: no-cachePragma: no-cacheSOAPAction: "initiate"Content-Length: 9307------=_Part_0_ 27211574.1133404205718Content-Type: text/xml; charset=UTF-8Content-Transfer-Encoding: binaryContent-Id: <F090DFD56421FD84AAF98C386AD50A44><?xml version="1.0" encoding="UTF-8"?><soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><soapenv:Body><initiate xmlns=""><payload xmlns="">1</payload><bin href="cid:1A744998BA8527BD121CAE96C022109F" xmlns=""/></initiate></soapenv:Body></soapenv:Envelope>------=_Part_0_ 27211574.1133404205718Content-Type: application/octet-streamContent-Transfer-Encoding: binaryContent-Id: <1A744998BA8527BD121CAE96C022109F> .....................
See Also: The following documentation:
|