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]

4 comments:

Anonymous said...

Thanks! This cures me my many recent headaches!!!

dollarMonger said...

Brilliant! This helped me a lot!

Padmarag said...

Thanks a lot. Very helpful.

Padmarag said...

Thanks a lot. Very helpful.

Jeff Sexton

007 1:144 Scale 1:350 Enterprise 10 Barrel Brewing 14 1856 2001 A Space Odyssey 3D modeling 40and20 4th of July 78 RPM Abyss Adam West Advertising Agora Models Air Canada Airline Airways Brewing Alan Bennett Alaska Alberta Alberta Street Pub Alfa Romeo Spider Touring Gran Sport Amati Amazon Amnesia Brewing AMT Analog signal Android Anomalies and Alternative Science Antiques Apache Apollo Apple Apple Stores Art Artisanal Beer Works Assembly Brewing Aston Martin Astoria Asus Atlas Audrey Augmented reality Aurora Famous Fighters auto-awesome Automobile Autos Aviary Aviation Backups Baelic Brewing Bale Breaker Brewing Bandai Barack Obama Barley Brown's Beer Bars Base Camp Brewing Batman Battery Beards Beer Beer Bar Bell System Bellwether Berkshire Hathaway Betty White Beyond the Fringe Bigfoot Bikes Bill Clinton Bird Food Bird Toys Birds Birthdays Bleriot Bleriot XI Block 15 Brewing Blogger Bojack Horseman Bolton Landing Brewing Boltons Boneyard Brewing Books Boxer Ramen Boxer Ramon Breakside Brewing Brian Eno Build Management Buoy Brewing Burger King Business and Economy Business Process Execution Language Bye & Bye Byte-order mark Canadian Carrot Cats Chex Mix Chihuly Chipmonk Christmas Civil Defense Clinton Clocks Closet Doors CNN Cockatiels Cocktails Collections Columbia Grafonola Columbia River George Columbia River Gorge Corners Corvallis County Cork Crooked Stave Brewing Crows Crux Brewing Cuisinart Culmination Brewing David Byne DB5 Dear Jane Debian Deschutes Brewing DFW C.V Diabetes Dick Curtis Digital Living Network Alliance Digital television Dinosaurs Disney Doll House Don the Beachcomber Double Mountain Brewing Dow Jones Industrial Average Dragons Dudley Moore Duesenburg SJ Roadster Durham University DVD E-mail address E9 Eagle Eagle Creek Fire Eaglemoss Easter ebauche Ecliptic Economics Ed Ed and Olive Eels EJB Elgin Elysian Brewing Energy development Enterprise Enterprise JavaBean ESP Evergreen Air Museum Everybody's Brewing Ex Novo Brewing F-84G Thunderjet Facebook Family Photos Fathers Day Fearless Brewing Fedora Ferment Brewing Ferns Festival of The Dark Arts Filesharing Finance Finger Firesign Theater Fireworks Flowers Flying Sub Food Ford Fort George Brewing Fossil fuel Free House Garfield James Abram Garfield Minus Garfield Gateway Brewing Gene Sexton Gene Wilder George Carlin Gigantic Brewing Gilgamesh Brewing Glass Glassfish Global warming Golden Arches Goldfinger Goofy Google Google Assistant Google Buzz Google Docs Google Home Google Lively Google Photos Google Reader Google Wave Google+ Goose Graffiti Grammar Gravy Great Divide Brewing Great Notion Brewing Greek Festival Greenhouse gas Gruen GT40 H. G. Wells Half-Life 2 Halloween Harlan Hawaii Helbros High-definition television Hilo Hilo Brewing History Ho 229 Hollywood Theater Hopworks Urban Brewery Horizon Models HP Hybrid electric vehicle IBM Impala Inner city Instagram Insulin Investing IPMS Iris Irony J.C. Penny James Bond Jane Austen Java Java Architecture for XML Binding JC Penny JDBC Jeannine Stahltaube Jeff's! Jim Davis joe the plumber John McCain Jonathan Miller Jubelale Kapaau Karma Kauai Kay Thompson Kermit the Frog Keys Keys Lounge Kids and Teens Kona LA Auto Show Labrewtory Larry King Laser Laserdisc Leavenworth Wenatchee River Level Brewing Lilly Tomlin linux Little Beast Brewing Lloyd Center Logging Lowry Sexton LPs Lucky Lab Magnets Mark Cuban Market trends Martin Mull Maytag McDonald Mediatomb Meier and Frank Mel Brooks Mercury Microsoft Microsoft Windows Migration Brewing Mobius Models modern Times Brewing Money Monkey monsters Moon MOUNT HOOD Mount Tabor Movie Reviews Multnomah Falls Music Music industry Muxtape MySQL NetBeans Netflix Nikon Nikon Z50 Ninkasi Brewing Nintendo Nissan Cube Norm Coleman North Bar Nuclear fallout Nuclear warfare Nuggest Nuts OBF Office Depot Offshoot Beer Co Oktoberfest Ola Brewing Old Town Brewing Olive Open ESB Oracle Corporation Orca Oregon Orion Space Clipper Owls Pacific Ocean Packard Boattail Pam American Parrots Patio Sale PDX Pearl District Pearl District Portland Oregon Peppers Performance Review Peter Cook Peter Iredale Pets Pfriem Brewing Philip K Dick Phone Book photography Pizza Plank Town Brewing Play Station PlayStation 3 pluspora Pocher Podcast Poke Pokémon HeartGold and SoulSilver Polar Lights Politics Pono Brewing Portal Portland Portland Development Commission Presidents Pride and Prejudice Programming Projects PS3 PS4 Pumpkins Quotation Marks Rad Power Radio Radio Room Ramen Ramon Recipes Recording Industry Association of America Renewable energy Reservoir Reuben's Brewing Reubens Brewing RIAA Richmond Pub Robot Chicken Rock-paper-scissors Rogue Brewing Round 2 Sales San Francisco Santa Sarcasm Sasquatch Brewing SATA Science fiction film Sea Quake Brewing Seattle Selfie Serbia Service-oriented architecture Seward Shelby Cobra Shipwreck Shopping Signs Silver Moon Brewing Slide Rule Snow Soap Soap Cutter Social Security Social Studies Society6 Sony Sopwith F.1 Camel BR.1 Soviet Space 1999 Space Race Spad XIII Speaker Repair Spirit of St. Louis Spitfire SQL Squirrel's St Patricks Day Stanford Hospital Star Trek Star Wars Starbucks Stock market Storm Breaker Strip search Stripes Studebaker Studellac Sun Microsystems Supernatural T-Mobile Tablet Tamiya Tamiya Spitfire Taube TechCrunch Technical Television Terminal Gravity Thanksgiving The Producers ThinkGeek Three Creeks Brewing Thunder Island Brewing Tiki Time Bandits Toaster Tom Peterson Tools Top Ferment Total Recall Transportation Security Administration Trumpeter Tubboat Tyco UFOs Unicode United States United States Department of Homeland Security Universal Plug and Play Unknown Primates USB USS Yorktown Valcom Van Gilder Hotel Vegetable garden VHS Victoria Video Video game Vintage Images Vintage Vintage! Virtual world Volcano Hawaii Volvo C70 Voyage to the Bottom of the Sea Wall Street War of the Worlds Warren Buffett Warrenton watches Watercolor Wayfinder Brewing We Can Remember It for You Wholesale Web service Web Services Description Language Whiskey Wii Windows 7 Windows Phone 7 Windows Vista Windows XP Windy Wingnut Wings Wood Worthy Brewing WWI WWII X-Files X-ray vision XML XML Schema Y2K Yeti YouTube Yugo Zero Mostel Zima Zoom H2n