Figure 5 - The offline Invoice Manager uses the same business objects used on the server, and receives invoices via invoice objects sent over the wire as XML. Fields in this form are bound to the business object, such as THISFORM.oInv.oData.InvDate and THISFORM.oInv.oCustomer.oData.Company.
The offline manager has a download link, which runs in a loop to download any unprocessed invoices from the server. The relevant import code for an individual invoice looks like this:
110811",; ,"username","password") loInv = CREATE("cInvoice") loInv.Load(0) && Load empty invoice & subitems loXML.lRecurseObjects = .T. loXML.XMLToObject(lcXML,loInv) ? loInv.oCustomer.oData.LastName ? loInv.oCustomer.oData.Company ? loInv.oData.InvDate ? loInv.oData.InvTotal ? loInv.oLineItems.ARows.Sku ? loInv.oLineItems.ARows.Descript
Again, notice how little code is involved here - 4 lines of code to download and import the object. However, in order for the import of this hierarchical object to work, a couple of pre-requisites are required. The objects must exist on the client side in order for wwXML to be able to figure out the structure of the object to import to - hierarchical objects cannot be dynamically created from an export DTD. Furthermore, the object structure must be prefilled with empty values of the proper types in order for the import to work.
Member objects imported to cannot be NULL or otherwise undefined, as that would prevent them from being parsed and filled. wwXML requires an object structure, as it works by walking through the object and looking for matching elements in the XML document. In the case of the LineItem array, at least the first item of the array must be prefilled with the proper type - in this case, an item object.
Notice the special call to loInv.Load(0). This method call actually handles creating the properly formatted 'empty' objects. If we look at the business object code's Load method we'll see the following:
Each object is pre-initialized as an empty object. This type of behavior is not only useful for wwXML's import parsing, but also for displaying the business objects in forms. For example, in the invoice manager form the actual field ControlSource values are bound to the object's properties. NULL values would cause all sorts of problems for the databinding.
We already know how to create the invoice XML easily - the process in the Invoice Manager client application is identical to what's happening on the server. What's different now is that we're sending data to the server:
The only new thing that happens here is that when we call wwXML::LoadUrl(), we're passing the XML generated from the invoice export as the second lcXML parameter, which is POSTed to the Web server. LoadURL() posts data in raw format and doesn't URLEncode the string. The server knows how to pick up this raw XML buffer. This simple bit of code handles posting an invoice from the client to the server. On the server, the code is straightforward:
Keep in mind that these types of replication scenarios require thought about synchronization: You may run into duplication of primary keys. This invoice object includes a method that checks to see whether an invoice PK exists already, and if necessary, reassigns the PK for the invoice and updates the lineitems accordingly. For the customer, if a PK exists already, it double checks to see if the name and address match the original record. If it doesn't, a new customer record is created instead. Conflict resolution is not always easy, as this type of online/offline application becomes an exercise in replication, along with all the issues related to this complex topic of synchronization of data.
I'm sure that you can think of some useful applications for this stuff. Using these strategies, you can very easily build powerful applications with existing business objects, which can be transferred painlessly over the wire. The possibilities are endless.
And remember, XML can be useful for lots of things, even in non-distributed applications. By persisting objects and tables as XML, you can pass data around in applications, and even save object state into a table's memo field.
In the next issue, we'll look at how to build XML-based services that provide data access and remote code activation to a variety of clients generically. I'll talk more on how to access and consume XML in browser applications, as well as fat client applications, using the same application logic. We'll also look at SOAP and how it figures in that space. Until then, give it a shot and put what you've learned to good use.
Rick Strahl is president of West Wind Technologies on Maui, Hawaii. The company specializes in Internet application development and tools focused on Internet Information Server, ISAPI, C++ and Visual FoxPro. Rick is author of West Wind Web Connection, a powerful and widely used Web application framework for Visual FoxPro, West Wind HTML Help Builder, co-author of Visual WebBuilder, a Microsoft Most Valuable Professional, and a frequent contributor to FoxPro magazines and books. He is co-publisher and co-editor of CoDe magazine, and his book, "Internet Applications with Visual FoxPro 6.0", has been recently released by Hentzenwerke Publishing. For more information please visit: http://www.west-wind.com/.