A user recently reported that he was unable to generate any dashboards while using DashConn within SAP Crystal Dashboard Design. This set me off on what turned out to be an interesting and surprising investigation.
My main lead in the investigation was that the user was working with the recently released Flash Player 11.0.1.152. Once I installed this update I also quickly reproduced the problem.
I found that an error:
TypeError: Error #1009: Cannot access a property or method of a null object reference.
was occurring in the DashConn code that was interacting with the Force.com Toolkit for Adobe AIR and Flex
Specifically this was occurring while iterating over the Field objects within a DescribeSObjectResult object.
for each(var fieldObj:Field in allSObjectFields) { if (fieldObj.name.toUpperCase() == fieldName.toUpperCase()) { return fieldObj; } }
Diagnosis
Looking at the source code for DescribeSObjectResult it was quite perplexing how null Field objects could be associated with the fieldArray property. These fields are always assigned through the call to a constructor. See lines 10 and 11.
public function DescribeSObjectResult(obj:ObjectProxy=null) { if (obj != null) { for (var key:String in obj) { var val:Object = obj[key]; if (val is ArrayCollection || val is ObjectProxy) { if (key == "fields") { var fieldArray:Array = new Array(); for (var i:int = 0;i<(val as ArrayCollection).length;i++) { var field:Field = new Field((val as ArrayCollection)[i]); fieldArray[field.name] = field; fieldArray.length++; } this[key] = fieldArray; } else if (key == "childRelationships") { var crArray:Array = new Array(); var cr:ChildRelationship; if ( val is ObjectProxy ) { cr = new ChildRelationship(val as ObjectProxy); crArray[cr.relationshipName] = cr; crArray.length++; } else { for (var i2:int = 0;i2<(val as ArrayCollection).length;i2++) { cr = new ChildRelationship((val as ArrayCollection)[i2]); crArray[cr.relationshipName] = cr; crArray.length++; } } this[key] = crArray; } else if (key == "recordTypeInfos") { var rtArray:Array = new Array(); var rt:RecordTypeInfo; if ( val is ObjectProxy ) { rt = new RecordTypeInfo(val as ObjectProxy); rtArray[rt.name] = rt; rtArray.length++; } else { for (var i3:int=0;i3<(val as ArrayCollection).length;i3++) { rt = new RecordTypeInfo((val as ArrayCollection)[i3]); rtArray[rt.name] = rt; rtArray.length++; } } this[key] = rtArray; } } else { this[key] = obj[key] } } }
Once I debugged into this code I discovered the surprise Flash Player had waiting — incrementing the length property on the Array was causing an additional key/value pair to be added to the Array with a key of 0 and a value of null!
Quick checks showed that this errant behavior also occurred with the other Array properties on DescribeSObjectResult. Finally I created a simple test case which shows that the problem is easily reproducible and occurs whenever the length property on an associative Array is incremented. I’ve submitted the issue to Adobe https://bugbase.adobe.com/index.cfm?event=bug&id=3020393 .
If you are reading this because you’ve also come across this problem I encourage you to vote for this on the Adobe site.
Work Around
Since properly executed byte code should not produce null Field object values I did not want to change my code in the many places that iterate over the associative array properties of fields and childRelationships. Instead I decided to post-process each DescribeSObjectResult immediately after it is retrieved from the request on the Connection. This required a code change in only a couple of spots.
public function fixupDescribeSObjectResult(sobjResult:DescribeSObjectResult):void { fixupAssociativeArray(sobjResult.childRelationships); fixupAssociativeArray(sobjResult.fields); fixupAssociativeArray(sobjResult.recordTypeInfos); } public function fixupAssociativeArray(values:Array):void { if (values != null) { delete values[0]; } }
Using Array objects to store key/value hashes is discouraged as an ActionScript 3 practise. Had the Force.com Toolkit for Adobe AIR and Flex followed the recommended approach of storing these in a plain Object this issue would have been avoided. Perhaps a future update to the Toolkit will make this change.