|
Targeted AudienceYou need to read this section only if you want to develop your own web services hosted on your own servers connected to a custom MAX application, which runs inside the Upvise mobile client. This basically allows you to host yourselves the persistant data used by your custom MAX Application.Upvise servers implements the server part of this protocol and offer a cloud-based persistant data storage and sync. The Upvise mobie client implement the client part of the protocol. It you want to create custom applications where persistant data us stored and sychronized with the Upvise servers, you can skip this section and learn how to write Upvise MAX Applications. Overviewthe MAX Protocol is used to synchronize data sources between the Upvise mobile client and a server storing the persistant data. The MAX Protocol is basically a super set of HTTP + RSS. It allows a 2 way synchronization of any data source specified in the Upvise MAX Application. It supports 2 key features:
For a given data source, you must implement a corresponding web service URL. This URL can be called in 2 different ways by the Upvise mobile cliemt:
How to define the web service url associated with a MAX local data source
For each data source described in a MAX Application, there is a webservice tag which
defines the assocated web service URL responsible to synchronize the data locally. <max>
...
<datasrc id="[MyDataSourceId]">
<webservice>http://myurl</webservice>
<field type="primarykey">id</field>
<field type="string">[FieldName1]</field>
<field type="string">[FieldName2]</field>
..... more fields here...
</datasrc>
....
</max>
Implementing the GET Query RequestThis query is responsible to send to the Upvise mobile client all newly created, modified and deleted records on the server, since the last time. This query is sent by the Upvise mobile client when the local data cache has expired. It performs an HTTP GET request using the url specified in the webservice tag of the data source. Two GET parameters are added in the URL:
http://mywebservice?lbd=[SomeDate]&auth=[SomeToken] When the web service receive this request, it must:
The XML Response must contain one root <rss> node and under it one <channel> node. Under it, you must have the follwoing nodes:
Each <item> node must contain sub nodes who names must match the field names defined in the MAX data source. A special case is for DELETED items. In this case, only the id sub node identifiying the record and a special sub node <_deleted>1<_deleted> must be set. <rss>
<channel>
<title>[MyDataSourceId]</title>
Note:
XML Response Details
Note : it is recommended to use a class or library which generates correctly formatted XML document, so that the output document will be valid, and correctly encoded. For example, with ASP.NET, use theXmlTextWriter class with the following declaration: XmlTextWriter myWriter = new XmlTextWriter(new MemoryStream(), new UTF8Encoding(false)); Implementing the POST request (INSERT, UPDATE and DELETE)
When data are created, updated or deleted locally on the device, the Upvise mobile clients performs a HTTP POST request on the web service URL.
There is one HTTP POST request for each created, modified or deleted record.
Server responsability:
Impact on server SQL Database tablesIf the server storage is going to be a SQL Database, you will need to keep track of 2 important pieces of data for each record you intend you synchronize:
The timestamp information for each record is essential because it enable your GET
Query Request to return only the modifed data since the last query. One simple way to do this is to add 2 system fields to each SQL table : a _date field of type Date and a _deleted field of type integer with a default value of 0
Encoding of Date Field ValuesDate values must always be in Epoch format, ie te number of milliseconds from Jn 1st 1970. It applies to:
Note . if you are using Microsft ASP.NET, you can convert a .NET DateTime object to and from a Epoch time string with the following code: public static long ToEpochTime(DateTime aDate) {
DateTime start = new DateTime(1970, 1, 1);
TimeSpan ts = aDate.Subtract(start);
return Convert.ToInt64(ts.TotalMilliseconds);
}
public static DateTime fromEpochTime(string aDate) {
long date = Convert.ToInt64(aDate);
DateTime start = new DateTime(1970, 1, 1);
return start.AddMilliseconds(date);
}
public static long EpochTimeNow() {
return ToEpochTime(DateTime.UtcNow);
}
Note:
Sessions State & CookiesThe MAX protocol does not support cookies. No cookies are sent by the Upvise client during the HTTP requests to the server web service. It means that all sessions state manangement based on cookies like ASP.NET or JSP session variable MUST NOT be used. For each request, the server is responsible to authenticate the client using the auth token each time. GZIP compression for the XML ResponseWeb server implements HTTP 1.1 compression in a standard way for dynamic or static pages. But you usually need to implement it manually for web services. Here is how to do it simply:
For example, with ASP.NET, use the following code: // data is supposeed to have been filled correctly with the XML Response public writeResponse(byte[] data) { // if the Upvise client accepts gzip encoding //and the plain data is bigger than 1KB, gzip the data first string acceptEncoding = Request.Headers["Accept-Encoding"]; if (!string.IsNullOrEmpty(acceptEncoding) && acceptEncoding.Contains("gzip") && data.Length> 1024) { // compress the data data = Compress(data); // set the gzip flag in the HTTP Response Response.AppendHeader("Content-Encoding", "gzip"); } // VERY IMPORTANT: set the No Transform flag in the HTTP Response Header // to avoid intermediate gateways to re transform the response Response.Cache.SetNoTransforms(); // Set Content Length HTTP Response header Response.AppendHeader("Content-Length", data.Length.ToString()); // output the data to the Http Response stream Response.BinaryWrite(data); Response.Flush(); // NB : putting Response.End() instead or .Flush generates an Exception } private static byte[] Compress(byte[] plainData) { // create a new MemoryStream to contain the zipped data MemoryStream memzip = new MemoryStream(); // Create a GZIP compression stream on top of it GZipStream zip = new GZipStream(memzip, CompressionMode.Compress); // write and flush the plain data into it zip.Write(plainData, 0, plainData.Length); zip.Flush(); // close the GZIP Stream to read from underlying stream //if not, the zip footer will not be written!!!! zip.Close(); // get the compressed data byte[] zippedData = memzip.ToArray(); memzip.Close(); return zippedData; } |
