To generate a SyncML document, an application first needs to initialize a new instance of the workspace using the smlInitInstance command. During initialization, a workspace buffer is assigned. The toolkit uses this workspace buffer to store the generated documents until they are sent or to store incoming documents until they are completely parsed. The application developer could choose to use one workspace for all documents, a different workspace for each document, or one workspace for generating documents and one for parsing documents. The type of encoding that the toolkit should use (XML or WBXML) can be set at the time the instance is initialized with smlInitInstance or at a later point in time with smlSetEncoding. It is also possible to define a fixed memory size for the workspace buffer. The process is outlined in Figure 10-5.
Figure 10-5. Example API sequence for creating and sending a SyncML document
The following example shows how the instance options are set for generating WBXML documents, assigning the name "MyWorkspace" to the workspace, and setting the size of the workspace to 20,000 bytes. The pointer &callbacks points to a structure containing the pointers to the callback functions. The ID of the instance is stored ininstanceID, which is needed by the application to identify the workspace to be used.
// Set instance options and callback functions instanceOptions.encoding = SML_WBXML; instanceOptions.workspaceName = "MyWorkspace"; instanceOptions.workspaceSize = 20000; callbacks.addCmdFunc = &handleAddCmdFunc; ... // Create instance rc = smlInitInstance(&callbacks, &instanceOptions, &instanceID);
While creating SyncML documents, the application uses straightforward toolkit commands derived from the SyncML DTD [SRP02]: smlStartMessage defines the beginning of a SyncML document. The smlStartSync and smlStartAtomic commands define the beginning of command sequences in the body of a document. For each command specified in the SyncML DTD, there is a dedicated API function, such as smlCopy or smlUpdate. The application is responsible for terminating each message, sync, or atomic sequence with a corresponding smlEndMessage, smlEndSync, or smlEndAtomic call.
The following code snippets show how an Add command is built and added into the XML document.
// Allocate needed variables AddPtr_t pElem; MemSize_t metaLen; sourceLen; String_t metaStr; sourceStr; // Allocate and set structure for Add command pElem = (AddPtr_t)smlLibMalloc(sizeof(Add_t)); smlLibMemset(pElem, 0, sizeof(Add_t)); pElem->elementType = SML_PE_GENERIC; // Build PcData element for CmdID "1" pElem->cmdID = (PcdataPtr_t)smlLibMalloc (sizeof(Pcdata_t)); smlLibMemset(pElem->cmdID, 0, sizeof(Pcdata_t)); pElem->cmdID->contentType = type; pElem->cmdID->length = smlLibStrlen("1"); pElem->cmdID->content = smlLibStrdup("1"); // Build itemList with one item pElem->itemList = (ItemListPtr_t)smlLibMalloc (sizeof(ItemList_t)); smlLibMemset(pElem->itemList, 0, sizeof (ItemList_t)); // Build item in itemList pElem->itemList->pitem = (ItemPtr_t)smlLibMalloc (sizeof(Item_t)); smlLibMemset(pElem->itemList->pitem, 0, sizeof (Item_t)); // Build target for item. Because it is an Add // command, the target is not known pElem->itemList->pitem->target = NULL; // Add the content pElem->itemList->pitem->data = smlString2Pcdata("Content"); // Add Source element pElem->itemList->pitem->source = (SourcePtr_t) smlLibMalloc(sizeof(Source_t)); smlLibMemset(pElem->itemList->pitem->source, 0, sizeof(Source_t)); // Allocate memory for locURI sourceLen = smlLibStrlen ("http://www.syncml.org/servlet/syncit/")+1; sourceStr = smlLibMalloc((MemSize_t)sourceLen); smlLibMemset(sourceStr, 0x00, sourceLen); smlLibStrcpy(sourceStr, "http://www.syncml.org/servlet/syncit/"); pElem->itemList->pitem->source->locURI = smlString2Pcdata(sourceStr); pElem->itemList->pitem->source->locName = NULL; // No flags, because response is required pElem->flags = 0; pElem->cred = 0; // Add the meta element for a vcalendar object metaLen = smlLibStrlen("<mi:type xmlns= 'syncml:metinf'>text/vcalendar</mi:type>")+1; metaStr = smlLibMalloc((MemSize_t)metaLen); smlLibMemset(metaStr, 0x00, metaLen); smlLibStrcpy(metaStr, "<mi:type xmlns= 'syncml:metinf'>text/vcalendar</mi:type>"); pElem->meta = smlString2Pcdata(metaStr); // Now everything is prepared for the toolkit to // generate the Add element smlAddCmd(instanceID, pElem);
The sample above uses some helper functions provided by the toolkit, like smlString2Pcdata, which generates a Pcdata object from a String. In production code, each of the steps involved in the preparation of the data structures for the Add command (for example, filling the Meta element structure or the target element) should be coded in separate functions. These elements are used by most of the SyncML commands; therefore these separate functions could be reused in most of them without duplicating code. In this example everything was done in one code block to facilitate reading.
After the SyncML document has been created, the application is able to access the workspace buffer containing the assembled SyncML document. This is done using a pointer to the first position of the buffer. The smlLockReadBuffer command locks the workspace for exclusive access by the application, and returns that pointer and the actual size of the created document.
The application can now copy the assembled document from the workspace buffer into some outgoing communication buffer. After completing this task, the application must call smlUnlockReadBuffer to unlock the workspace and to pass the number of bytes that have been successfully retrieved from the workspace. The toolkit then removes those bytes from the workspace and can reuse the space. After smlUnlockReadBuffer is called, the toolkit gets back the responsibility for the workspace buffer. Now this SyncML instance is idle and can be used to perform any other new request.
When an instance is not needed any longer, the instance has to be terminated with smlTerminateInstance.