Error Codes of the SOAP Driver - Part 1

0 Likes
Error Codes from a SOAP Driver:

Novell Identity Manager has a bunch of drivers. The documentation for the various functionality in the drivers has varied from version to version. As a person who has to implement and deeply understand the inner workings of these drivers I often find the documentation lacking in things I would like to see.



Personally I would like to see the troubleshooting section of each driver doc enlarged to the point where it is possibly larger than the rest of the document. After all, we spend more time troubleshooting issues, in the real world, than in implementing the drivers. Or at least it feels that way.



You would think a link to articles or TIDs at least would be handy to have in the docs. Alas, I also understand that documentation writers cannot really satisfy this requirement of mine, but that does not mitigate the need for such documentation. Thankfully Novell has the Cool Solutions approach, where community contributed content can try and help cover some of the weak areas.



Since Novell will not fix the issue, I have decided to put my money where my mouth is, and do my best to help out.



When I work with a driver, I almost always run into some kind of problem. The trick is, find the problem in trace, copy the text of it, and save it somewhere. Then you have a record of what happened, and how you fixed it.



I have written a number of articles on a variety of drivers, trying to show sample error documents, explain what happened, and how you might fix it, using my approach of collecting error messages. You can read some of those at:



AD Driver:

Active Directory Driver Error Messages - Part 1

Active Directory Driver Error Messages - Part 2

Active Directory Driver Error Messages - Part 3

Active Directory Driver Error Messages - Part 4

Active Directory Driver Error Messages - Part 5



eDir Driver:

Error Codes of the eDirectory Driver for Identity Manager - Part 1



GW Driver:

Troubleshooting GroupWise Driver External User Creation



JDBC Driver:

Error Codes of the Novell Identity Manager Driver for JDBC: Part 1 of 4

Error Codes of the Novell Identity Manager Driver for JDBC: Part 2 of 4

Error Codes of the Novell Identity Manager Driver for JDBC: Part 3 of 4

Error Codes of the Novell Identity Manager Driver for JDBC: Part 4 of 4



SAP HR driver:

Error Codes of the SAP HR driver for Identity Manager - Part 1



User App:

User App, SOAP Integration activity interesting error



There are more generic troubleshooting tips in my collection of articles as well, but this is mainly a list of the specific error code articles.



Recently I was working on a SOAP driver to talk to Salesforce.com, and had collected some errors I would like to talk about. You can read my series on building such a SOAP driver from scratch at the links below. Now while the series focused on Salesforce.com's particular API, the information in the article can be extrapolated to most any SOAP interface you need to work with.


Getting Started Building a SOAP Driver for IDM - Part 1

Getting Started Building a SOAP Driver for IDM - Part 2

Getting Started Building a SOAP Driver for IDM - Part 3

Getting Started Building a SOAP Driver for IDM - Part 4

Getting Started Building a SOAP Driver for IDM - Part 5

Getting Started Building a SOAP Driver for IDM - Part 6

Getting Started Building a SOAP Driver for IDM - Part 7

Getting Started Building a SOAP Driver for IDM - Part 8

Getting Started Building a SOAP Driver for IDM - Part 9



Some of these errors are specific to Salesforce.com, and others are more generic and related to SOAP in general. So lets dig into some error messages.



I thought this was an interesting error. This is very much analogous to how User Applications approval and request forms are represented in a Start Workflow token. That is, when you use the Start Workflow token in IDM Policy, you need to provide all the valid data for all the available forms. If the data provided will not work, then the workflow will not actually start.



In SFDC, you are allowed to add validation to fields on the web interface. Well we were updating a field called DP_1__c, (all custom attributes in SFDC end in under bar, under bar, c (__c)) that had some validation on it, requiring a value.



Well I tried sending an event to clear it on an instantiated object, which would have left it without such a value, but the web interface validation rules will not allow that, and feed back the error in response to the attempt to make the change via a SOAP call. I had not expected this level of integration, which was actually pretty neat.



<nds dtdversion="2.0">
<source>
<product build="201006032211 Internal Novell build. Not for production use." instance="SOAP-SPML" version="3.5.5">Identity Manager Driver for SOAP</product>
<contact>Novell, Inc.</contact>
</source>
<output>
<soapenv:Envelope xmlns="urn:enterprise.soap.sforce.com" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Body>
<upsertResponse>
<result>
<created>false</created>
<errors>
<fields>DP_1__c</fields>
<message>DP must be populated in order to save this record.</message>
<statusCode>FIELD_CUSTOM_VALIDATION_EXCEPTION</statusCode>
</errors>
<id xsi:nil="true"/>
<success>false</success>
</result>
</upsertResponse>
</soapenv:Body>
</soapenv:Envelope>
</output>
</nds>


You can see from the XML that the <statusCode> node tells us it is a custom field validation error, and from the <message> node, you can see specifically that you cannot have a blank DP_1__c field. This is quite nice, and easy to fix and resolve. Make sure to set a DP_1__c value, or at least, do not try and clear them!



Next up was writing to a field that is operational, and the system maintains. In fact it turns out in this case, the web interface even has a little lock icon next to the field, so I should have known better. It was LastModifiedByID actually, and we really wanted to be able to write back who last approved the change in workflow. But turns out that just was not going to work. In fact, the way SFDC works, even if that had been allowed, the ID we were logged in with (our service account) would have overwritten the value, as soon as it was written, as of course the real LastModifiedByID value was our service account, writing some other value. A bit circular, but I had hoped it would work for a moment there.



<nds dtdversion="2.0">
<source>
<product build="201006032211 Internal Novell build. Not for production use." instance="SOAP-SPML" version="3.5.5">Identity Manager Driver for SOAP</product>
<contact>Novell, Inc.</contact>
</source>
<output>
<soapenv:Envelope xmlns="urn:enterprise.soap.sforce.com" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Body>
<upsertResponse>
<result>
<created>false</created>
<errors>
<fields>LastModifiedById</fields>
<message>Unable to create/update fields: LastModifiedById. Please check the security settings of this field and verify that it is read/write for your profile.</message>
<statusCode>INVALID_FIELD_FOR_INSERT_UPDATE</statusCode>
</errors>
<id xsi:nil="true"/>
<success>false</success>
</result>
</upsertResponse>
</soapenv:Body>
</soapenv:Envelope>
</output>
</nds>


But the error message that you is really nice. Better in fact than the way eDirectory responds. eDirectory and IDM treat operations as atomic, that is, the whole change gets a single error. Alas it does not identify which attribute was the problem when you get something like a -613 Syntax violation, or -609 Missing Mandatory. That would be really helpful in IDM, to isolate the problem faster. Currently you need to read really carefully and see if you can see the error in the last <add> or <modify> event that was sent into the directory. Often it is hard to see, and takes a second set of eyes to really locate.



This next error was a cute one. Mistake in my code, sent in an incomplete modify event, which SFDC calls upsert() a compacting of UPdate and inSERT. I thought it was an interesting response to note. You can see how incomplete the upsert() call is and the response. The specific error is that no class (sObjects) was sent, but of course much more was missing as well.



Bad upsert() document:



<nds dtdversion="3.5" ndsversion="8.x">
<source>
<product version="3.6.11.4904">DirXML</product>
<contact>Novell, Inc.</contact>
</source>
<input>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Header>
<urn:SessionHeader xmlns:urn="urn:enterprise.soap.sforce.com">
<urn:sessionId>00DT0000000G9Rh4xWmeC4nwnipskcCB.4ff7HGZXntzLUZAfeAxCLIMT.vWVxPyejntunWLGv</urn:sessionId>
</urn:SessionHeader>
</soapenv:Header>
<soapenv:Body>
<urn:upsert xmlns:urn="urn:enterprise.soap.sforce.com">
<urn:externalIDFieldName>Id</urn:externalIDFieldName>
<urn:sObjects/>
</urn:upsert>
</soapenv:Body>
</soapenv:Envelope>
</input>
</nds>
[08/06/10 15:13:34.332]:sfdc ST: Remote Interface Driver: Document sent.

Error document returned:

[08/06/10 15:13:34.672]:sfdc :Remote Interface Driver: Received.
[08/06/10 15:13:34.673]:sfdc :
<nds dtdversion="2.0">
<source>
<product build="201006032211 Internal Novell build. Not for production use." instance="SOAP-SPML" version="3.5.5">Identity Manager Driver for SOAP</product>
<contact>Novell, Inc.</contact>
</source>
<output>
<soapenv:Envelope xmlns:sf="urn:fault.enterprise.soap.sforce.com" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Body>
<soapenv:Fault>
<faultcode>sf:INVALID_TYPE</faultcode>
<faultstring>INVALID_TYPE: Must send a concrete entity type.</faultstring>
<detail>
<sf:InvalidSObjectFault xsi:type="sf:InvalidSObjectFault">
<sf:exceptionCode>INVALID_TYPE</sf:exceptionCode>
<sf:exceptionMessage>Must send a concrete entity type.</sf:exceptionMessage>
<sf:row>-1</sf:row>
<sf:column>-1</sf:column>
</sf:InvalidSObjectFault>
</detail>
</soapenv:Fault>
</soapenv:Body>
</soapenv:Envelope>
</output>
</nds>


Just for completeness, here is what a successful upsert() should look like. You can see that the sObjects value is set with an XML attribute of type="Contact", and that we have an <Id> node (since we said a line or two before that the externalIDFieldName was Id) with what looks like a database Id, and we have an attribute to change (Type_of_Log__c) and a value for it (Debug).




<nds dtdversion="3.5" ndsversion="8.x">
<source>
<product version="3.6.11.4904">DirXML</product>
<contact>Novell, Inc.</contact>
</source>
<input>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Header>
<urn:SessionHeader xmlns:urn="urn:enterprise.soap.sforce.com">
<urn:sessionId>00DT0000000GV1Q!AR4AQJFDbCTP5RLVSkDcJi7cWgymQKSYOH9Rh4xWmeCMT.vWVxPyejntunWLGv</urn:sessionId>
</urn:SessionHeader>
</soapenv:Header>
<soapenv:Body>
<urn:upsert xmlns:urn="urn:enterprise.soap.sforce.com">
<urn:externalIDFieldName>Id</urn:externalIDFieldName>
<urn:sObjects xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="Contact">
<Id>a0CT00000034PLLMA2</Id>
<Type_of_Log__c>Debug</Type_of_Log__c>
</urn:sObjects>
</urn:upsert>
</soapenv:Body>
</soapenv:Envelope>
</input>
</nds>


Now a SOAP related Remote loader error that was interesting happened, when I tried to start a second instance of a SOAP driver Remote loader on the same server. The error is a bit misleading, at first glance, and requires an understanding of how the SOAP driver normally works on the Publisher channel.



The Subscriber channel of a SOAP driver makes sense. You connect to an HTTP server and POST an XML document, and then get a response back on the same connection.



But the Publisher channel is a little different than usual. In fact, the Publisher channel is really a SOAP listener, such that you could set up a web services end point with it. Alas there is no easy way to define a WSDL to give to someone with the driver, but basically the shim will take whatever XML it gets in the document, and dump it into the engine to process. This is how the SPML and DSML configurations work. In that case, SPML and DSML are defined standards that have defined WSDL's so Novell implemented managing them via XSLT and Policy.



Thus when you start a SOAP driver it is set to listen on some address and port combination. By default that is 127.0.0.1 and port 18180, which you can see in the trace below quite clearly. since the Remote Loader (at the right trace level, I was using 3) shows the configuration options it has received from the driver, and there it is:



<pubHostPort display-name="Listening IP address and port">127.0.0.1:18180</pubHostPort>


Thus when I copied my driver into a second instance, I took this configuration setting with it as well, and of course, now I am trying to listen with two processes on the same IP and port combination which never seems to work well.



Here we see the remote loader trying start to start the shim up, loading the configuration options sent by the engine.



DirXML: [08/09/10 16:33:09.87]: TRACE:  <nds dtdversion="3.5" ndsversion="8.x">
<source>
<product version="3.6.11.4904">DirXML</product>
<contact>Novell, Inc.</contact>
</source>
<input>
<init-params src-dn="\IDVAULT-LAB\com\ICAP\Drivers\IDM\SFDC-User">
<driver-filter>
<allow-class class-name="User">
<allow-attr attr-name="Username"/>
<allow-attr attr-name="Name"/>
<allow-attr attr-name="FirstName"/>
<allow-attr attr-name="Id"/>
<allow-attr attr-name="CreatedById"/>
<allow-attr attr-name="CreatedDate"/>
<allow-attr attr-name="LastModifiedById"/>
<allow-attr attr-name="LastModifiedDate"/>
<allow-attr attr-name="Email"/>
<allow-attr attr-name="LastName"/>
<allow-attr attr-name="Phone"/>
</allow-class>
</driver-filter>
<publisher-options>
<pubHostPort display-name="Listening IP address and port">127.0.0.1:18180</pubHostPort>
<pubAuthID display-name="Authentication ID">localsoapuser</pubAuthID>
<pubAuthPwd display-name="Authentication Password" is-sensitive="true" type="password-ref"/>
<pubResultContentType display-name="Content-Type">text/xml</pubResultContentType>
<KMOName display-name="KMO name"></KMOName>
<pubKeystoreFile display-name="Keystore file"></pubKeystoreFile>
<pubKeystorePassword display-name="Keystore password"></pubKeystorePassword>
<pubServerKeyAlias display-name="Server key alias"></pubServerKeyAlias>
<pubServerKeyPassword display-name="Server key password"></pubServerKeyPassword>
<pubRequireMutualAuth display-name="Require mutual authentication">false</pubRequireMutualAuth>
<heartbeat display-name="Heartbeat interval in minutes">1</heartbeat>
</publisher-options>
</init-params>
</input>
</nds>
DirXML: [08/09/10 16:33:09.87]: TRACE: SFDC-User: pubHostPort = 127.0.0.1:18180



But we get the following error, since some other process is already listening on that port:



DirXML: [08/09/10 16:33:09.92]: TRACE:  <nds dtdversion="2.0">
<source>
<product build="201006032211 Internal Novell build. Not for production use." instance="SFDC-User" version="3.5.5">Identity
Manager Driver for SOAP</product>
<contact>Novell, Inc.</contact>
</source>
<output>
<status level="fatal" type="driver-general">
<description>Address already in use</description>
</status>
</output>
</nds>
DirXML: [08/09/10 16:33:09.92]:
DirXML Log Event -------------------
Driver = \IDVAULT-LAB\com\ICAP\Drivers\IDM\SFDC-User
Thread = Publisher
Level = fatal
Message = <description>Address already in use</description>


The tricky part about this, is that usually when a Remote Loader reports this kind of error it is referring to the listen port for the Remote Loader. The default is 8190, that the Remote Loader listens, for the engine to contact it on. In this case, it is the SOAP listener, trying to use a defined address and port.




In my series on building a SOAP driver, I talked a fair bit about how to manage query events. It turns out, I probably spent more time on this functionality than on any other single piece. Mostly because there are so many different and interesting ways you might get a query and trying to handle all of them. You can see an example where I did not quite correctly handle converting the XDS query into a SOAP query event.



<nds dtdversion="3.5" ndsversion="8.x">
<source>
<product version="3.6.11.4904">DirXML</product>
<contact>Novell, Inc.</contact>
</source>
<input>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Header>
<urn:SessionHeader xmlns:urn="urn:enterprise.soap.sforce.com">
<urn:sessionId>00DT0000000GV1Q!AR4AQJFDbCTP5RLVSkDcJi7cWgymQKSYOH9Rh4xWmeC4nwnipskcCB.4ff7HGZXntzLUZAfeAxCLIMT.vWVxPyejntunWLGv</urn:sessionId>
</urn:SessionHeader>
<urn:QueryOptions xmlns:urn="urn:enterprise.soap.sforce.com">
<urn:batchSize>2000</urn:batchSize>
</urn:QueryOptions>
</soapenv:Header>
<soapenv:Body>
<urn:query xmlns:urn="urn:enterprise.soap.sforce.com">
<urn:queryString>Select , a.Id FROM User a WHERE (a.Id!=null AND a.ID='00520000001LLL5AAG' )</urn:queryString>
</urn:query>
</soapenv:Body>
</soapenv:Envelope>
</input>
</nds>


You can see that I got most of it right. The error is here in the queryString node:



<urn:queryString>Select , a.Id FROM User a WHERE (a.Id!=null AND a.ID='00520000001LLL5AAG' )


I sent in an extra comma, probably because of trying to parse out the list of attributes and not doing it right.



SFDC returns the following error, which is again, really nicely informative!



<nds dtdversion="2.0">
<source>
<product build="201006032211 Internal Novell build. Not for production use." instance="SFDC-User" version="3.5.5">Identity Manager Driver for SOAP</product>
<contact>Novell, Inc.</contact>
</source>
<output>
<soapenv:Envelope xmlns:sf="urn:fault.enterprise.soap.sforce.com" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Body>
<soapenv:Fault>
<faultcode>sf:MALFORMED_QUERY</faultcode>
<faultstring>MALFORMED_QUERY:
Select , a.Id FROM User a WHERE (a.Id!=null
^
ERROR at Row:1:Column:7
unexpected token: ','</faultstring>
<detail>
<sf:MalformedQueryFault xsi:type="sf:MalformedQueryFault">
<sf:exceptionCode>MALFORMED_QUERY</sf:exceptionCode>
<sf:exceptionMessage>
Select , a.Id FROM User a WHERE (a.Id!=null
^
ERROR at Row:1:Column:7
unexpected token: ','</sf:exceptionMessage>
<sf:row>1</sf:row>
<sf:column>7</sf:column>
</sf:MalformedQueryFault>
</detail>
</soapenv:Fault>
</soapenv:Body>
</soapenv:Envelope>
</output>
</nds>


This is how you should do it! The exception code MALFORMED QUERY is useful, and then showing the error with a marker in the response! What a great way to report errors! I wish eDirectory could do something like this. Heck, they even format it easy to read. I mean, come on! There are a whole series of these exception codes that I managed to generate, but alas lost the file they were stored in so I will not be able to show them all.



In part 8 and 9 of my series on building a SOAP driver to connect to SFDC, I discussed the getUpdated() function call, and how it might be used. You can see that you need to specify a sObjectType (like in the upsert() error from above), a startDate and an endDate. If you look, you will see the two times are about 15 seconds apart. Turns out SFDC does not like that. It wants at least one minute of range, before it will query for updated objects. Interestingly enough, if you read the error message, it looks to me like it is backwards. It says "startDate must be at least one minute greater than endDate" but should that not be the other way around? I.e. Start date should be 1 minute BEFORE end date. I.e. In the past. The message would have you believe that Start date should be after end date, which makes little to no sense. Always fun finding little errors like this in someone else's code!



<nds dtdversion="3.5" ndsversion="8.x">
<source>
<product version="3.6.11.4904">DirXML</product>
<contact>Novell, Inc.</contact>
</source>
<input>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Header>
<urn:SessionHeader xmlns:urn="urn:enterprise.soap.sforce.com">
<urn:sessionId>00DT0000000GV1Q!AR4AQJFDbCTP5RLVSkDcJi7cWgymQKSYOH9Rh4xWmeC4nwnipskcCB.WLGv</urn:sessionId>
</urn:SessionHeader>
</soapenv:Header>
<soapenv:Body>
<urn:getUpdated xmlns:urn="urn:enterprise.soap.sforce.com">
<urn:sObjectType>Bank_Code__c</urn:sObjectType>
<urn:startDate>2010-08-16T18:39:02-00:00</urn:startDate>
<urn:endDate>2010-08-16T18:39:17.808Z</urn:endDate>
</urn:getUpdated>
</soapenv:Body>
</soapenv:Envelope>
</input>
</nds>
[08/16/10 14:37:36.307]:sfdc PT: Remote Interface Driver: Document sent.
[08/16/10 14:37:37.656]:sfdc :Remote Interface Driver: Received.
[08/16/10 14:37:37.656]:sfdc :
<nds dtdversion="2.0">
<source>
<product build="201006032211 Internal Novell build. Not for production use." instance="SOAP-SPML" version="3.5.5">Identity Manager Driver for SOAP</product>
<contact>Novell, Inc.</contact>
</source>
<output>
<soapenv:Envelope xmlns:sf="urn:fault.enterprise.soap.sforce.com" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Body>
<soapenv:Fault>
<faultcode>sf:INVALID_REPLICATION_DATE</faultcode>
<faultstring>INVALID_REPLICATION_DATE: startDate must be at least one minute greater than endDate</faultstring>
<detail>
<sf:UnexpectedErrorFault xsi:type="sf:UnexpectedErrorFault">
<sf:exceptionCode>INVALID_REPLICATION_DATE</sf:exceptionCode>
<sf:exceptionMessage>startDate must be at least one minute greater than endDate</sf:exceptionMessage>
</sf:UnexpectedErrorFault>
</detail>
</soapenv:Fault>
</soapenv:Body>
</soapenv:Envelope>
</output>
</nds>



As you can see, there are a bunch of different errors here, in different styles. Some from the Remote Loader, some from the engine, some that are very specific to Salesforce.com's specific SOAP end point. Nonetheless, the hope is that should you run into any of these errors, you should be able to search using Google and find this document, which hopefully can help you.



I greatly encourage everyone as they work on a driver that is new to them, or even an old familiar driver, to watch for interesting errors in DSTrace,collect them, and consider writing them up like this. If nothing else, the next time you run into the error, and can remember seeing it before, but not what the issue was, how you fixed it, or any other details, you should be able to find your article and remind yourself. Thats what I do all the time. I see a problem, I cannot remember the specifics of how I fixed it, but I know I wrote about it, so I go find and read the article I wrote to remind myself. It works!

Comment List
Related
Recommended