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.
4 comments:
Thanks! This cures me my many recent headaches!!!
Brilliant! This helped me a lot!
Thanks a lot. Very helpful.
Thanks a lot. Very helpful.
Post a Comment