SMP3’s OData SDK for Android
I am a big fan of the ODataSDK. It is running very stable and is well structured. Of course there are still some limitations, but the current release can already be a great help in building OData based native apps.
You might know this ODataSDK Overview picture, which is originally posted inside the official documentation:
Source: SyBooks Online
It gives you a good overview of the components/libraries which are included. The ODataSDK is available for iOS and Android and will be installed using the SMP3 MobileSDK installer (available for Mac OS and Windows). The installer will “only” extract the content to a folder, it won’t install you a development environment. So you have to use your own environment, e.g. eclipse with Google’s ADT plugin in case of Android or XCode in case of iOS.
If you want to use the Android ODataSDK simply start a new project in eclipse and import the ODataSDK libraries. Your OData app need to follow these steps:
- Register/Onboard the user
The UserManager class can be used to do this, it can be done either synchronously or asynchronously (you need to define a listener for the response).
After the registration you will have an APPCID (Application Connection ID). This APPCID will be attached (automatically from the SDK) to each subsequent OData requests to SMP3. In this way SMP3 can identify the device/app. You as a developer have to persist the APPCID, e.g. in SAP’s DataVault or in SharedPreferences.
The registration process needs to be executed the first time/initial time the app got started. On subsequent app starts load the stored APPCID and add it to the ClientConnection object of the ODataSDK.
- Getting Data
After you are successfully onboarded you have to perform the following tasks
- Getting Service Document
You need to perform a GET request against smpserver:8080/com.sap.sample.app/ to get the Service Document. The Parser (part of ODataSDK) has a method to parse the Service document.
- Getting MetaData Document
After that you need to perform a GET request against smpserver:8080/com.sap.sample.app/$metadata to get the Metadata Document or sometimes called Schema Document. The Parser can parse the MetaData Document, but needs as input the parsed Service Document. That is the reason why you have to query for the Service Document before.
- Getting OData
Finally, you can make calls GET (or others like POST/PUT/DELETE) to get concrete data, e.g. against a OData collection smpserver:8080/com.sap.sample.app/ProductCollection/. The Parser need as input the parsed MetaData document and the name of the Collection.
All the requests can be built using the RequestManager and BaseRequest object. All requests are sent asynchronously, you can set for each request a listener, which will implement an onError and an onSuccess method. In both callback methods you will get the original Request object as well as the Response object. To determine which response got received you can use the method request.setRequestTag during the request creation and then in the callback method ask for this request tag. Alternatively, you can parse the request URL (e.g. url ending with $metadata, so it should be a MetaData document). You need to distinguish between the different types (Service Document, MetaData Document and concrete OData), because the OData Parser has to be called with different methods depending on the document type (parser.parseODataServiceDocument(EntityUtils.toString(response.getEntity(), “UTF-8”))). The “response” object is the HTTP response object you will receive in the onSuccess() method. Think about to choose the encoding, e.g. UTF-8 to avoid errors with umlauts (ä, ü, ö or other special characters). The Parser can parse OData either represented in JSON or XML (Atom).
- Getting Service Document
- Caching the Data
The Offline OData Cache has been newly introduced in SMP3’s OData SDK. It allows you to store OData documents for offline use. Underlying the ODataSDK this data is stored in an encrypted SQLite database. Not the content itself (as it is the case in the DataVault) is encrypted, the whole database is encrypted using SQLCipher.
To use the Offline Caching mechanism you need to define get an encryptionKey from the EncryptionKeyManager (part of the ODataSDK). It is in the responsibility of the Developer to store this encryptionkey for subsequent use. This means if you restart the app you need to provide the encryption key manually to the EncryptionKeyManager. A good way to store this key is using SAP’s DataVault.
To understand the concept of OData Offline be aware, that it is offline caching, you cannot access objects inside the database with SQLQueries, Views or something else. So if you want to display a list of products, then you would query the OData service to get the ProductCollection. After you received this collection you can store this ODataFeed inside the Cache. For each ODataEntry (an ODataFeed is a list of ODataEntry objects) a new row will be added with the same URL_KEY (a string specified when storing the document). This means when we are offline we can only query the cache for the document which was stored under the same URL_KEY key. So it will give us back all Products. We cannot create a query in the way SELECT * FROM Products WHERE COUNTRY = ‘GERMANY’. This is currently not possible. So if you want to allow these kind of actions when you are offline you have two possibilities
- Preemptive Caching
If you know that only the products in Germany are interesting, then directly ask this (when you are online) query to the OData service in a way like this ODataService/ProductCollection/?$filter=country eq ‘Germany’. And then store this ODataFeed under a new URL_Key. If you are offline you can read this list of ODataentry objects from cache and you can display them directly.
- In-Memory Search
When you stored the whole ProductCollection, then you can read this from the Cache, and manually search through the list of ODataEntry objects to find the objects which fulfill the condition getPropertyValue(“Country”) == “Germany”. But be aware, this might be very time consuming and also the heap usage can be large (dependent on the size of your ProductCollection)
Of course before choosing either possibility a) or b) you can always check if the user is really offline, if he is online then simply query the OData service and everything is good…
- Preemptive Caching
- Using Delta Queries
So, let’s say we downloaded a large collection of products and we cached this product list. How can we update it?
Therefore, the Delta Query functionality got introduced. Starting from SAP Netweaver Gateway 2.0 SP07 an OData service can implement delta queries. This means for each GET request to the OData service, the service will responds with the data and an additional delta link. The delta link is a mix of an identifier and a timestamp. Next time the device wants to request data it will send a GET request including the delta link and the ODataService will respond only these objects that were new, updated or deleted (according to the timestamp inside the delta link). In this way you will massively save time for getting the data and also on the device for parsing the data. The response can be merged to the existing data by using the Cache object. But consider the fact, that the developer of the OData service has to implement this delta functionality, either by using Delta Exchange Tables (coming from Syclo framework) or by its own (e.g. by calling a BAPI which takes the timestamp as input and is returning the corresponding items).
- Queuing Offline Requests
Besides caching OData (results from GET operations) offline, you can also queue POST/PUT or DELETE requests. So if you want to create a new object (e.g. a new Product to the ProductCollection) you will use the class BaseRequest to construct a POST request, which includes the IODataEntry object that you want to create. If you are sending the request and the ODataSDK recognized that you are offline this request will be put in a queue. The queue is realized as a table inside the encrypted database and for each new request a new row is created. The ODataSDK recognizes the NetworkState, because there is a Listener implemented which you also have to define in your AndroidManifest file (com.sap.mobile.lib.request.NetworkStateListener). As soon as you are coming online again, the ODataSDK RequestManager will send the requests stored in the queue (you can provide priorities to the order of sending…).
When you want to use this mechanism you have to consider a few things (as a developer):
The Cache object distinguish between two kinds of offline cached objects: Server entries and local entries. Server entries are OData documents that were coming from the OData service (GET responses) and which were manually stored inside the Cache. Local Entries are POST/PUT/DELETE requests which are sent when the device is offline.
The Server entries (cache.readEntriesServer) and the local (not synced) entries (cache.readEntriesLocal) have to be merged by your own before you display data in your app. Because if the user adds a product, he expects to see the new created product directly in his list of products, it doesn’t matter if he is online or not. You have to manually delete the entries from the local cache after you received the confirmation (e.g. HTTP 201 Created) from SMP. If you are not deleting the request from the local entries you might display the product twice in the productlist, one object coming from the local entries and the other one coming from the server…
I hope that this blog post let you understand the concept of SMP3’s ODataSDK and particular Offline OData a bit more.