Jeff Sexton

Friday, January 09, 2009

OpenESB BPEL Assignment Tips

Understanding how Assign operations occur in BPEL can save a lot of headaches, and coding. One of my favorite run-time exception messages from BPEL is this:

ElementNotFoundInComplexTypeException: Particle not found in the complex type.

Assignment problems are commonly at the root of this error, and there are some facts about the Assign operations that are extremely helpful to know.

1. When assigning a fragment of XML from one variable to another, where the schemas of the two variables are in different namespaces, it is usually required to assign each leaf individually.

2. Data can be copied from one part to another, however the source namespace will be preserved, likely leading to undesired results.

3. Repeating nodes should be copied with a loop because any BPEL evaluation must result in a single node, however this specification is not strictly enforced in OpenESB and an Assign will copy recursively through nodes that occur multiple times. Note again, however, that this leaves
the data in the source namespace.

4. Optional nodes be copied at the leaf level by setting "Ignore Missing From Data" to "yes" on the Assign operation. This applies in items 2 and 3 above also.

5. To copy leaf nodes in a repeating set, a loop construct is needed.

Consider the following example which will demonstrate these Assign properties. Two schemas, in two different namespaces, one WSDL using the schemas and input and output respectively, and a BPEL containing an Assign operation.

Here is the input schema.

<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://xml.netbeans.org/schema/inputXmlSchema"
xmlns:tns="http://xml.netbeans.org/schema/inputXmlSchema"
elementFormDefault="qualified">
<xsd:element name="Sample" type="tns:SampleType"/>
<xsd:complexType name="SampleType">
<xsd:sequence>
<xsd:element name="Result" type="xsd:short"/>
<xsd:element name="Message" type="xsd:string" minOccurs="0"/>
<xsd:choice minOccurs="0" maxOccurs="unbounded">
<xsd:element name="ID1" maxOccurs="unbounded"
type="xsd:long"/>
<xsd:element name="ID2" maxOccurs="unbounded"
type="xsd:long"/>
<xsd:element name="Data1" type="tns:Data1Type"/>
<xsd:element name="Data2" type="tns:Data2Type"/>
</xsd:choice>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="Data2Type">
<xsd:sequence>
<xsd:element name="FirstName" type="xsd:string"/>
<xsd:element name="LastName" type="xsd:string"/>
<xsd:element name="MiddleName" minOccurs="0"
type="xsd:string"/>
<xsd:element name="BirthDate" minOccurs="0" type="xsd:date"/>
<xsd:element name="Data2Field" minOccurs="0"
maxOccurs="unbounded" type="tns:field"/>
<xsd:element name="ID" minOccurs="0" maxOccurs="unbounded"
type="xsd:long"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="Data1Type">
<xsd:sequence>
<xsd:element name="FirstName" type="xsd:string"/>
<xsd:element name="LastName" type="xsd:string"/>
<xsd:element name="MiddleName" minOccurs="0"
type="xsd:string"/>
<xsd:element name="BirthDate" minOccurs="0" type="xsd:date"/>
<xsd:element name="Data2Field" minOccurs="0"
maxOccurs="unbounded" type="tns:field"/>
<xsd:element name="ID" minOccurs="1" maxOccurs="1"
type="xsd:long"/>
<xsd:element name="Data1Field" minOccurs="0"
maxOccurs="unbounded" type="tns:field"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="field">
<xsd:sequence>
<xsd:element name="Name" type="xsd:string"/>
<xsd:choice>
<xsd:element name="BinValue" type="xsd:base64Binary"/>
<xsd:element name="StringValue" type="xsd:string"/>
<xsd:element name="DateValue" type="xsd:date"/>
<xsd:element name="NumericValue" type="xsd:decimal"/>
</xsd:choice>
</xsd:sequence>
</xsd:complexType>
</xsd:schema>

And the output schema:

<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://xml.netbeans.org/schema/outputXmlSchema"
xmlns:tns="http://xml.netbeans.org/schema/outputXmlSchema"
elementFormDefault="qualified">
<xsd:element name="Sample" type="tns:SampleType"/>
<xsd:complexType name="SampleType">
<xsd:sequence>
<xsd:element name="Result" type="xsd:short"/>
<xsd:element name="Message" type="xsd:string" minOccurs="0"/>
<xsd:choice minOccurs="0" maxOccurs="unbounded">
<xsd:element name="ID1" maxOccurs="unbounded"
type="xsd:long"/>
<xsd:element name="Data1" type="tns:Data1Type"/>
</xsd:choice>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="Data1Type">
<xsd:sequence>
<xsd:element name="FirstName" type="xsd:string"/>
<xsd:element name="LastName" type="xsd:string"/>
<xsd:element name="MiddleName" minOccurs="0"
type="xsd:string"/>
<xsd:element name="BirthDate" minOccurs="0" type="xsd:date"/>
<xsd:element name="Data2Field" minOccurs="0"
maxOccurs="unbounded" type="tns:field"/>
<xsd:element name="ID" minOccurs="1" maxOccurs="1"
type="xsd:long"/>
<xsd:element name="Data1Field" minOccurs="0"
maxOccurs="unbounded" type="tns:field"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="field">
<xsd:sequence>
<xsd:element name="Name" type="xsd:string"/>
<xsd:choice>
<xsd:element name="BinValue" type="xsd:base64Binary"/>
<xsd:element name="StringValue" type="xsd:string"/>
& <xsd:element name="DateValue" type="xsd:date"/>
<xsd:element name="NumericValue" type="xsd:decimal"/>
</xsd:choice>
</xsd:sequence>
</xsd:complexType>
</xsd:schema>

The output schema is a proper sub-set of the input in that all it's elements match elements of the input, however some input elements are not present in the output. ID2 for example my occur in the input, but not in the output. In addition, the output and the input have different target namespaces.

The following trivial BPEL will be used for these example:


The following Assign operation is setup initially:


This runs fine, and appears to copy input data to the output as needed. For example consider the following input.

<soapenv:Body> <inp:Sample> <inp:Result>1</inp:Result> <inp:Message>Hello</inp:Message> <inp:Data1> <inp:FirstName>Steve</inp:FirstName> <inp:LastName>Austin</inp:LastName> <inp:Data1Field> <inp:Name>One</inp:Name> <inp:StringValue>One String</inp:StringValue> </inp:Data1Field> <inp:Data1Field> <inp:Name>Two</inp:Name> <inp:NumericValue>365</inp:NumericValue> </inp:Data1Field> </inp:Data1> </inp:Sample>

And this resulting output.

<ns1:Sample xmlns:inp="http://xml.netbeans.org/schema/inputXmlSchema" xmlns:msgns="http://j2ee.netbeans.org/wsdl/operationWSDL" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://xml.netbeans.org/schema/outputXmlSchema"> <inp:Result>1</inp:Result> <inp:Message>Hello</inp:Message> <inp:Data1> <inp:FirstName>Steve</inp:FirstName> <inp:LastName>Austin</inp:LastName> <inp:Data1Field> <inp:Name>One</inp:Name> <inp:StringValue>One String</inp:StringValue> </inp:Data1Field> <inp:Data1Field> <inp:Name>Two</inp:Name> <inp:NumericValue>365</inp:NumericValue> </inp:Data1Field> </inp:Data1> </ns1:Sample>

All the data is there, however the namespace in the out output is that of the input, "http://xml.netbeans.org/schema/inputXmlSchema". A conventional unmarshaller, constructed using the output schema, applied to this data will result in an empty structure because this message contains no data in the output name space.

How to copy the data into the output namespace? The data must be copied at the leaf level. Consider the following updated Assign operation.


This fails.

<SOAP-ENV:Fault>
<faultcode>SOAP-ENV:Server</faultcode>
<faultstring>BPCOR-6135:A fault was not handled in the process
scope; Fault Name is
{http://docs.oasis-open.org/wsbpel/2.0/process/executable}selectionFailure;
Fault Data is null. Sending errors for the pending requests in the process scope before terminating the process instance</faultstring>
<faultactor>sun-bpel-engine</faultactor>
<detail>
<detailText>BPCOR-6135:A fault was not handled in the process scope; Fault Name is
{http://docs.oasis-open.org/wsbpel/2.0/process/executable}selectionFailure;
Fault Data is null. Sending errors for the pending requests in the process scope before terminating the process instance
Caused by: I18N: BPCOR-3025: Selection Failure occurred in
BPEL({http://enterprise.netbeans.org/bpel/BpelModule1/BPELProcess}BPELProcess)
at line 30!
BPCOR-6129:Line Number is 20
BPCOR-6130:Activity Name is Assign1</detailText>
</detail>
</SOAP-ENV:Fault>

The problem is that Data1 can not be copied in this way. It must be copied at the leaf level. What's more the input, above, does not include an instance of ID1. Removing the assignments of Data1 and ID1, and re-running the above input works however, even though Data1 elements are
no longer created in the output:

<ns1:Sample
xmlns:msgns="http://j2ee.netbeans.org/wsdl/operationWSDL"
xmlns:ns1="http://xml.netbeans.org/schema/outputXmlSchema">
<ns1:Result>1</ns1:Result>
<ns1:Message>Hello</ns1:Message>
</ns1:Sample>

First, how to copy ID1 as a leaf, when it may or may not exist? One way is to test its existence in an if construct and copy it conditionally, but with complex data this really is not practical. A better way is to set "Ignore Missing From Data" to "yes" on Assign1. First, add an assignment
of the ID1 node to Assign1. Next, in the BPEL builder, go to design mode and select the Assign operation. In the Navigator view, select the assignment of ID1. You can tell which one it is by looking at the properties view.



In the properties view, change "Ignore Missing From Data" to "yes". Here is what the resulting BPEL source will look like:

<assign name="Assign1">
<copy>
<from>$OperationWSDLOperationIn.part1/ns0:Result</from>
<to>$OperationWSDLOperationOut.part1/ns1:Result</to>
</copy>
<copy>
<from>$OperationWSDLOperationIn.part1/ns0:Message</from>
<to>$OperationWSDLOperationOut.part1/ns1:Message</to>
</copy>
<copy ignoreMissingFromData="yes">
<from>$OperationWSDLOperationIn.part1/ns0:ID1</from>
<to>$OperationWSDLOperationOut.part1/ns1:ID1</to>
</copy>
</assign>


The service now runs without error and ID1 will only appear in the output if it occurs in the input. Getting Data1 copied requires a bit more work. Firstly, if Data1 was defined to only occur once. Then assigning each leaf element inside Data1 would work, provided that these assignments where all set to "Ignore Missing From Data". The "Ignore Missing From Data" setting would be needed because Data1 includes optional elements, so not all its possible elements will exist. Since Data1 elements can occur multiple times, a loop construct is needed. This example will use a ForEach with a second Assign. To accomplish this, the ForEach and the
second Assign are added using the BPEL editor. Next, predicates are added to Data1 on both the input and the output. The predicate is the ForEach counter variable. Lastly the ForEach is set to loop from 1 to the count of Data1 nodes in the input. The second Assign copies Data1 elements and needs to have ignoreMissingFromData="yes" on the copies for its optional
fields. Nested complex elements DataField1 and DataField2 are left off the assignment for now.


The resulting assingment will work even though some fields no not exist in the input. Those fields will only appear in the output if they occur in the input. Here is the ForEach source.

<forEach name="ForEach1" parallel="no" counterName="ForEach1Counter"> <startCounterValue>1</startCounterValue>

<finalCounterValue>count($OperationWSDLOperationIn.part1/ns0:Data1)</finalCounterValue>
<scope name="Scope1">
<assign name="Assign2">
<copy ignoreMissingFromData="yes">

<from>$OperationWSDLOperationIn.part1/ns0:Data1[$ForEach1Counter]/ns0:FirstName</from>

<to>$OperationWSDLOperationOut.part1/ns1:Data1[$ForEach1Counter]/ns1:FirstName</to>
</copy>
<copy ignoreMissingFromData="yes">

<from>$OperationWSDLOperationIn.part1/ns0:Data1[$ForEach1Counter]/ns0:LastName</from>

<to>$OperationWSDLOperationOut.part1/ns1:Data1[$ForEach1Counter]/ns1:LastName</to>
</copy>
<copy ignoreMissingFromData="yes">

<from>$OperationWSDLOperationIn.part1/ns0:Data1[$ForEach1Counter]/ns0:MiddleName</from>

<to>$OperationWSDLOperationOut.part1/ns1:Data1[$ForEach1Counter]/ns1:MiddleName</to>
</copy>
<copy ignoreMissingFromData="yes">

<from>$OperationWSDLOperationIn.part1/ns0:Data1[$ForEach1Counter]/ns0:BirthDate</from>

<to>$OperationWSDLOperationOut.part1/ns1:Data1[$ForEach1Counter]/ns1:BirthDate</to>
</copy>
<copy ignoreMissingFromData="yes">

<from>$OperationWSDLOperationIn.part1/ns0:Data1[$ForEach1Counter]/ns0:ID</from>

<to>$OperationWSDLOperationOut.part1/ns1:Data1[$ForEach1Counter]/ns1:ID</to>
</copy>
</assign>
</scope>
</forEach>

The output created from the same input used above now includes a Data1 element. Because of the loop, more than one Data1 would be copied if more than one was provided in the input.

<ns1:Sample xmlns:msgns="http://j2ee.netbeans.org/wsdl/operationWSDL" xmlns:ns1="http://xml.netbeans.org/schema/outputXmlSchema"> <ns1:Result>1</ns1:Result> <ns1:Message>Hello</ns1:Message> <ns1:Data1> <ns1:FirstName>Steve</ns1:FirstName> <ns1:LastName>Austin</ns1:LastName> </ns1:Data1> </ns1:Sample>

Data1's included name/value pair, Data1Field is ignored in Assign2. Data1Field has a required name, and a choice construct for data of several types. Choice constructs can be handled exactly like elements that may occur zero times - with leaf level Assign operations having ignoreMissingFromData="yes". Since Data1 may include zero to many Data1Field instances, this assignment must occur in a loop construct similar to the one above. This is left as an exercise for the reader.

Reblog this post [with Zemanta]

Post a Comment
3D modeling Advertising Air Canada Airline Alfa Romeo Spider Touring Gran Sport Analog signal Android Anomalies and Alternative Science Apache Apollo Astoria Augmented reality Aurora Famous Fighters auto-awesome Automobile Autos Barack Obama Batman Beards Beer Bell System Berkshire Hathaway Bigfoot Bird Toys Birds Blogger Books Build Management Business and Economy Business Process Execution Language Byte-order mark Canadian Carrot Cats Christmas Civil Defense CNN Cockatiels Collections Crows Dear Jane Debian Diabetes Digital Living Network Alliance Digital television Disney Doll House Dow Jones Industrial Average Duesenburg SJ Roadster Durham University E-mail address ebauche Economics EJB Energy development Enterprise JavaBean ESP Facebook Fedora Filesharing Finance Ford Fossil fuel Garfield James Abram Garfield Minus Garfield Glassfish Global warming Golden Arches Goofy Google Google Buzz Google Docs Google Lively Google Photos Google Reader Google Wave Google+ Greenhouse gas Half-Life 2 Helbros High-definition television History Hybrid electric vehicle IBM Inner city Instagram Insulin Investing Irony J.C. Penny Jane Austen Java Java Architecture for XML Binding JDBC Jeff's! Jim Davis joe the plumber John McCain Karma Kay Thompson Kermit the Frog Kids and Teens LA Auto Show Larry King Laser Logging Lowry Sexton Mark Cuban Market trends McDonald Meier and Frank Microsoft Microsoft Windows Models Monkey monsters Moon MOUNT HOOD Music Music industry Muxtape MySQL NetBeans Netflix Nintendo Nissan Cube Norm Coleman Nuclear fallout Nuclear warfare Office Depot Open ESB Oracle Corporation Pacific Ocean Packard Boattail Pearl District Pearl District Portland Oregon Philip K Dick photography PlayStation 3 Pocher Pokémon HeartGold and SoulSilver Politics Portal Portland Portland Development Commission Presidents Pride and Prejudice Programming Projects Radio Recording Industry Association of America Renewable energy RIAA Robot Chicken Rock-paper-scissors Sarcasm Science fiction film Serbia Service-oriented architecture Shopping Slide Rule Social Security Social Studies Society6 Spirit of St. Louis SQL Stanford Hospital Star Wars Starbucks Stock market Strip search Sun Microsystems T-Mobile TechCrunch Technical ThinkGeek Toaster Total Recall Transportation Security Administration Unicode United States United States Department of Homeland Security Universal Plug and Play Unknown Primates Vegetable garden Video game Vintage Images Vintage Vintage! Virtual world Volvo C70 Wall Street Warren Buffett watches We Can Remember It for You Wholesale Web service Web Services Description Language Wii Windows 7 Windows Phone 7 Windows Vista Windows XP X-Files X-ray vision XML XML Schema YouTube Yugo Zima