{"id":112,"date":"2005-12-31T09:55:05","date_gmt":"2005-12-31T13:55:05","guid":{"rendered":"http:\/\/www.dr-chuck.com\/wordpress\/?p=112"},"modified":"2011-12-17T12:18:51","modified_gmt":"2011-12-17T16:18:51","slug":"web-services-in-objective-c-part-2","status":"publish","type":"post","link":"https:\/\/www.dr-chuck.com\/csev-blog\/2005\/12\/web-services-in-objective-c-part-2\/","title":{"rendered":"Web Services in Objective C Part 2"},"content":{"rendered":"<p>This is the SoapUtils.h &#8211; you include this.<br \/>\nAgain if someone knows how to make this a set of utilities in a separate file that are linked together &#8211; let me know.  I am an Xcode Newbie :)<\/p>\n<p><!--more--><br \/>\n\/*<br \/>\n*  SoapUtils.h<br \/>\n*  SakaiDesktop<br \/>\n*<br \/>\n*  Created by Charles Severance on 12\/29\/05.<br \/>\n*  Copyright 2005 __MyCompanyName__. All rights reserved.<br \/>\n*<br \/>\n*\/<br \/>\n#include <Carbon\/Carbon.h><br \/>\n#define cleanupErr(err) \\<br \/>\nwhile (err != noErr) { fprintf(stderr, &#8220;Failed at line %d, error %d\\n&#8221;, __LINE__, err); goto cleanup; }<br \/>\nstatic OSStatus getUserRecordField(const AEDesc* record, const char* fieldName, OSType desiredType, void* buffer, Size bufferSize, Size* actualSize)<br \/>\n{<br \/>\n\/\/ Extract the userfield list from the record.<br \/>\nAEDesc userField;<br \/>\nOSStatus err = AEGetParamDesc(record, keyASUserRecordFields, typeAEList, &#038;userField);<br \/>\ncleanupErr(err);<br \/>\n\/\/ The userfield is an array of name,data, so we compare the name against what we&#8217;re looking for,<br \/>\n\/\/ and if it maches return nameIndex + 1 coerced appropriately<br \/>\nlong count;<br \/>\nerr = AECountItems(&#038;userField, &#038;count);<br \/>\ncleanupErr(err);<br \/>\nfor (long i = 1;  i <= count;  i += 2) {\nchar tmpName[255];\nSize tmpNameSize;\nOSType tossKeyword;\nOSType tossType;\nerr = AEGetNthPtr(&#038;userField, i, typeChar, &#038;tossKeyword, &#038;tossType, tmpName, sizeof(tmpName), &#038;tmpNameSize);\ncleanupErr(err);\nif (strncmp(fieldName, tmpName, tmpNameSize) == 0) {\nerr = AEGetNthPtr(&#038;userField, i + 1, desiredType, &#038;tossKeyword, &#038;tossType, buffer, bufferSize, actualSize);\ncleanupErr(err);\nbreak;\n}\n}\ncleanup:\nreturn err;\n}\nstatic void dumpDebug(const char* msg, const AppleEvent* event, OSType parameter)\n{\nfprintf(stderr, \"%s:\\n\", msg);\nAEDesc paramDesc;\nOSErr err = AEGetParamDesc(event, parameter, typeChar, &#038;paramDesc);\nif (err != noErr)\nfprintf(stderr, \"\\tCan't get parameter %4.4s - %d returned\\n\", &#038;parameter, err);\nelse {\nint len = AEGetDescDataSize(&#038;paramDesc);\nchar* buffer = new char[len];\nAEGetDescData(&#038;paramDesc, buffer, len);\nchar* p = buffer;\nchar* pEnd = buffer + len;\nwhile (p < pEnd) {\nchar* pNext = strpbrk(p, \"\\r\\n\");\nif (pNext == NULL)\npNext = pEnd;\nelse {\nwhile (pNext < pEnd &#038;&#038; (*pNext == '\\r' || *pNext == '\\n')) {\n*pNext++ = '\\0';\n}\n}\nfprintf(stderr, \"\\t%.*s\\n\", pNext - p, p);\np = pNext;\n}\nAEDisposeDesc(&#038;paramDesc);\ndelete[] buffer;\n}\nfprintf(stderr, \"\\n\\n\");\n}\n\/* Sample code\n\/\/ Java Signature in jws\n\/\/    public String mymethod(String id, Integer i, Double d)\nOSErr err;\nAEDesc soapParms;\nerr = createSoapParameters(&#038;soapParms);\nerr = addSoapParameter(&#038;soapParms, \"id\", typeChar, \"fred\");\nint i=25;\nerr = addSoapParameter(&#038;soapParms, \"i\", typeSInt32, &#038;i);\ndouble d=123.456;\nerr = addSoapParameter(&#038;soapParms, \"d\", typeIEEE64BitFloatingPoint, &#038;d);\nchar buffer[100000];\nchar errorMessage[1024];\nerr = makeSoapCall(url, \"mymethod\", &#038;soapParms ,buffer, sizeof(buffer), errorMessage, sizeof(errorMessage), false, true);\nAEDisposeDesc(&#038;soapParms);\nif ( err == noErr ) {\nfprintf(stderr,\"Success String = %s!\\n\\n\", buffer);\n} else {\nfprintf(stderr,\"Error String = %s!\\n\\n\", errorMessage);\n}\n*\/\nstatic OSStatus createSoapParameters(AEDesc* termsList)\n{\nOSErr err = AECreateList(NULL, 0, false, termsList);\nreturn err;\n}\nstatic OSStatus addSoapParameter(AEDesc* termsList, const char* name, OSType dataType, const void* data)\n{\nOSErr err = AEPutPtr(termsList, 0, typeChar, name, strlen(name));\nif ( err != noErr ) return err;\nswitch (dataType) {\ncase typeChar:\nerr = AEPutPtr(termsList, 0, typeChar, data, strlen((const char*)data));\nbreak;\ncase typeSInt32:\nerr = AEPutPtr(termsList, 0, typeSInt32, data, 4);\nbreak;\ncase typeIEEE64BitFloatingPoint:\nerr = AEPutPtr(termsList, 0, typeIEEE64BitFloatingPoint, data, 8);\nbreak;\ndefault:\nfprintf(stderr,\"Error, unknown type in addSoapParameter: %4s\\n\\n\",dataType);\n}\nreturn err;\n}\nstatic OSErr makeSoapCall(const char* url, const char* methodName, AEDesc* soapParms,\nchar* buffer, int bufferSize,\nchar *errorMessage, int errorMessageLength,\nint doDebug, int doDebugError )\n{\nif ( bufferSize < 1 || errorMessageLength < 1 ) return eLenErr;\nerrorMessage[0] = '\\0';   \/\/ No Error Message\nOSErr err;\nAEDesc targetAddress;\n\/\/ Create the target address\nerr = AECreateDesc(typeApplicationURL, url, strlen(url), &#038;targetAddress);\ncleanupErr(err);\nAppleEvent event;\nerr = AECreateAppleEvent(kAERPCClass, kAESOAPScheme, &#038;targetAddress, kAutoGenerateReturnID, kAnyTransactionID, &#038;event);\ncleanupErr(err);\nAEDisposeDesc(&#038;targetAddress);\n\/\/ Create the parameters for the event - the direct object is a record that contains\n\/\/ the method name, and a list of parameters\nAEDesc directObject;\nerr = AECreateList(NULL, 0, true, &#038;directObject);\ncleanupErr(err);\n\/\/ Put the method name\nerr = AEPutParamPtr(&#038;directObject, keyRPCMethodName, typeChar, methodName, strlen(methodName));\ncleanupErr(err);\nAEDesc paramRecord;\nerr = AECreateList(NULL, 0, true, &#038;paramRecord);\nif (err == noErr)\nerr = AEPutParamDesc(&#038;paramRecord, keyASUserRecordFields, soapParms);\n\/\/ Put the parameter record into the direct object\nerr = AEPutParamDesc(&#038;directObject, keyRPCMethodParam, &#038;paramRecord);\ncleanupErr(err);\nAEDisposeDesc(&#038;paramRecord);\n\/\/ Additional pieces for soap are the method namespace, namespaceURI and\n\/\/ SOAPAction.  If the SOAPAction isn't explicitly specified, it\n\/\/ will be the path part of the url (in this case, \"\/examples\")\n\/\/ You don't need to supply the method namespace...\n\/\/ static const char* methodNameSpaceURI = \"http:\/\/www.soapware.org\/\";\n\/\/ err = AEPutParamPtr(&#038;directObject, keySOAPMethodNameSpaceURI, typeChar, methodNameSpaceURI, strlen(methodNameSpaceURI));\n\/\/ cleanupErr(err);\n\/\/ Put the direct object into the event\nerr = AEPutParamDesc(&#038;event, keyDirectObject, &#038;directObject);\ncleanupErr(err);\nAEDisposeDesc(&#038;directObject);\n\/\/ Request debugging information\nif ( doDebug ) {\nSInt32 debugAttr = kAEDebugXMLDebugAll;\nerr = AEPutAttributePtr(&#038;event, keyXMLDebuggingAttr, typeSInt32, &#038;debugAttr, sizeof(debugAttr));\n}\n\/\/ Finally, send the event (we're using AESendMessage so we don't link against Carbon, just ApplicationServices)\nAppleEvent reply;\nAEInitializeDescInline(&#038;reply);\nerr = AESendMessage(&#038;event, &#038;reply, kAEWaitReply, kAEDefaultTimeout);\ncleanupErr(err);\n\/\/ The direct object contains our result (the name of the state)\nSize actualSize;\n\/\/ See if we got a return value\nerr = AEGetParamPtr(&#038;reply, keyDirectObject, typeChar, NULL, buffer, bufferSize, &#038;actualSize);\n\/\/ Upon success return after optionally dumping information\nif ( err == noErr ) {\nif ( actualSize < bufferSize ) {\nbuffer[actualSize] = '\\0'; \/\/ Terminate\n} else {\nbuffer [ bufferSize-1 ] = '\\0';\nerr = eLenErr;\nif ( errorMessageLength > 60 ) sprintf(errorMessage,&#8221;SOAP return buffer too short partial results returned, length= %d&#8221;, bufferSize);<br \/>\n}<br \/>\n}<br \/>\n\/\/ If we still have no error, lets return<br \/>\nif ( err == noErr ) {<br \/>\nif ( doDebug ) {<br \/>\ndumpDebug(&#8220;HTTP POST header&#8221;, &#038;reply, keyAEPOSTHeaderData);<br \/>\ndumpDebug(&#8220;XML Request&#8221;, &#038;reply, keyAEXMLRequestData);<br \/>\ndumpDebug(&#8220;HTTP Reply header&#8221;, &#038;reply, keyAEReplyHeaderData);<br \/>\ndumpDebug(&#8220;XML Reply&#8221;, &#038;reply, keyAEXMLReplyData);<br \/>\n}<br \/>\nreturn err;<br \/>\n}<br \/>\n\/\/ Dump debug information in this error case<br \/>\nif ( doDebug || doDebugError ) {<br \/>\ndumpDebug(&#8220;HTTP POST header&#8221;, &#038;reply, keyAEPOSTHeaderData);<br \/>\ndumpDebug(&#8220;XML Request&#8221;, &#038;reply, keyAEXMLRequestData);<br \/>\ndumpDebug(&#8220;HTTP Reply header&#8221;, &#038;reply, keyAEReplyHeaderData);<br \/>\ndumpDebug(&#8220;XML Reply&#8221;, &#038;reply, keyAEXMLReplyData);<br \/>\n}<br \/>\n\/\/ Something is wrong, lets whip up a nice return error string for them<br \/>\nerr = AEGetParamPtr(&#038;reply, keyErrorString, typeChar, NULL, errorMessage, errorMessageLength, &#038;actualSize);<br \/>\n\/\/ If the host is up, the web service is right, and the method name is right,<br \/>\n\/\/ and a parameter name is wrong (i.e. bad signature) &#8211; we come here with<br \/>\n\/\/ A 1701 (errAEDescNotFound &#8211; Descriptor Record Not Found)<br \/>\n\/\/ There may be other scenarrios that get us here &#8211; but for now, lets return the best<br \/>\n\/\/ error message we know about &#8211; it is better than a generic &#8220;interal fault 1701&#8243;<br \/>\nif ( err != noErr ) {<br \/>\nif ( errorMessageLength > 60 ) sprintf(errorMessage,&#8221;No Matching Web Service: %i\\n\\n&#8221;,err);<br \/>\n} else { \/\/ Normal error situation, like AxisFault, Error message already set<br \/>\n\/\/ Host does not exist: Transport error!<br \/>\n\/\/ Host is up, but no web service: java.io.FileNotFoundException: \/SakaiLogin.jws!<br \/>\n\/\/ Host is up, no matching method: No such operation &#8216;login&#8217;!<br \/>\n\/\/ force error return<br \/>\nerr =  errAEDescNotFound;<br \/>\n}<br \/>\ncleanup:<br \/>\nif ( doDebug || doDebugError ) {<br \/>\nfprintf(stderr,&#8221;makeSoapCall() error=%d url=%s (%s) message=%s\\n&#8221;,err,url,methodName, errorMessage);<br \/>\n}<br \/>\nreturn err;<br \/>\n}<\/p>\n","protected":false},"excerpt":{"rendered":"<p>This is the SoapUtils.h &#8211; you include this. Again if someone knows how to make this a set of utilities in a separate file that are linked together &#8211; let me know. I am an Xcode Newbie :)<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-112","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/www.dr-chuck.com\/csev-blog\/wp-json\/wp\/v2\/posts\/112","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.dr-chuck.com\/csev-blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.dr-chuck.com\/csev-blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.dr-chuck.com\/csev-blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.dr-chuck.com\/csev-blog\/wp-json\/wp\/v2\/comments?post=112"}],"version-history":[{"count":1,"href":"https:\/\/www.dr-chuck.com\/csev-blog\/wp-json\/wp\/v2\/posts\/112\/revisions"}],"predecessor-version":[{"id":2206,"href":"https:\/\/www.dr-chuck.com\/csev-blog\/wp-json\/wp\/v2\/posts\/112\/revisions\/2206"}],"wp:attachment":[{"href":"https:\/\/www.dr-chuck.com\/csev-blog\/wp-json\/wp\/v2\/media?parent=112"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.dr-chuck.com\/csev-blog\/wp-json\/wp\/v2\/categories?post=112"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.dr-chuck.com\/csev-blog\/wp-json\/wp\/v2\/tags?post=112"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}