Skip to content

Troubleshooting with ILOData

Interactive Local OData (ILOData) is a command line utility that lets you execute OData requests, upload, and download against a local offline store.

The ILOData command line utility is shipped as part of the SAP SDK. In the SAP BTP SDK for Android, it is located in the tools\ilodata folder of the installer. The SAP BTP SDK for iOS also provides an optional way to install the tools using the iOS Assistant. In the Mobile Development Kit, it is located in the MDKCLientSDK/Tools/ilodata folder of the installer.

ILOData functions in the same way as an offline OData client, without the need for an application or device, enabling you to test data from the back-end service.

If an offline store exists, ILOData uses it. If not, ILOData performs a download to create the offline store. To perform a download, you must set defining_query and service_root. If no defining queries are specified on the command line and no offline store exists, you are prompted to enter defining queries before ILOData opens the offline store.

To use ILOData against an existing offline store, you must first retrieve the underlying offline store files from the device they were created on. Offline store files include two database files, prefixed with the store name (or the default name if no store name was specified when the offline store was created). One has a .rq .udb suffix and the other has a .udb suffix. These files are located in the directory provided in the store_path option, or in the application data directory by default if no store_path has been provided.

Syntax

To view detailed help for the command line utility, use --help option.

ilodata --help
ILOData.exe --help
# -? is a shorthand for --help on Windows
ILOData.exe -?

The command line utility accepts parameters in the form of option=value. The valid options are listed below.

ilodata [option=<value> [option=<value> ...]]
ILOData.exe [option=<value> [option=<value> ...]]

Options

  • host The host name of the mobile services server.

  • port The port of the mobile services server.

  • appcid The application client ID.

    Note

    This is required by SAP Mobile Platform server and you DO NOT need to specify it for SAP Business Technology Platform server.

  • custom_cookie A custom cookie to add to all HTTP communications. Use this option multiple times if you have more than one custom cookie to add. Custom cookies are added to HTTP requests between the offline store and mobile services and to HTTP requests between mobile services and the back-end OData service. For example, custom_cookie=name1:value1;.

  • custom_header A custom header to add to all HTTP communications. Use this option multiple times if you have more than one custom header to add. Custom headers are added to HTTP requests between the offline store and SAP Mobile Services and to HTTP requests between mobile services and the back-end OData service. For example, custom_header=name1:value1;.

  • defining_query An OData read request that targets the OData endpoint that is associated with the offline store and retrieves a subset of the data, either during initialization of the offline store or during a download. Multiple defining queries can be defined for each OData endpoint by specifying multiple defining_query options on the command line. Defining queries become fixed for an offline store after the first time the store is opened.

    If no defining queries are provided on the command line, and no database files exist, ILOData prompts you to specify them. You can specify a name for the defining query (the name that would be used for a selective download and/or in an application configuration .ini file on the mobile services), as well as specify whether to download media streams. When the defining queries are specified using command line options, media streams are not downloaded by default, and the defining queries's names are the index at which they are specified. For example, in the following ILOData command, the first defining query would be named “1” and the second defining request would be named “2”.

    ILOData defining_query=Events defining_query=Sessions
    
  • enable_https Whether to use HTTP or HTTPS to communicate with mobile services. The default is no.

  • enable_repeatable_reqs Whether the back-end OData service supports repeatable requests. Repeatable requests (or idempotent requests) are a feature of some OData services that ensure OData requests are applied only once, even if they are received multiple times. This is useful in cases where OData responses may be lost due to intermittent network connectivity and is required by the offline store to guarantee that requests are applied exactly once to the back-end OData service.

  • extra_stream_parms Additional advanced stream parameters.

  • identity_file A file containing a client authentication certificate.

  • identity_password The password to use with the client authentication certificate.

  • log_file A file in which to log output.

  • prompt Override the default ILOData prompt.

  • retry_download The number of times to retry a download or store open when a failure occurs. The default is 0 (no retry).

  • retry_wait The amount of time, in milliseconds, to wait before retrying a download. The default is 100.

  • service_root The service root of the OData service. When the server is an instance of mobile services, this value depends on the Rewrite Mode configuration in the server-side application. If Rewrite Mode is set to Rewrite URL, then this is the name of the application back-end connector. If Rewrite Mode is set to Rewrite URL on Back End, then this is the path of the back-end OData endpoint. For example, if the application is configured with a connector named myconn to an OData endpoint of http://myhost:80/odata/endpoint, then the service_root should be myconn for Rewrite URL and odata/endpoint for Rewrite URL on Back End.

    Note

    The mobile service does not provide the functionality to use No Rewriting mode to support external back ends for offline usage.

  • store_key The key that encrypts the local database. By default the database is not encrypted.

  • store_name An arbitrary name that identifies the offline store. If omitted, the default name lodata is used.

  • store_path The file system path to store the offline store files. The default is the current directory.

  • trusted_certificate A file containing a trusted root certificate.

  • url_suffix The URL suffix path to the server. Specify the suffix to add to the URL of each HTTP request sent to the server.

  • username The user name to authenticate with the OData service.

  • password The password to authenticate with the OData service.

  • enable_undo_local_creation Specifies whether or not deleting an entity that was created locally but not yet uploaded undoes the creation. The default value is no.

  • enable_transaction_builder Specifies whether or not to enable the transaction builder. The default value is no.

  • add defining query Adds a new defining query.

Formats of Data Used with ILOData

  • null | NULL (case sensitive)

  • binary: 0xhhhhhh (hh is a pair of hexadecimal digits)

  • boolean: true | false

  • byte, sbyte, int16, int32, int64: [0-9]*

  • datetime: yyyy-mm-dd[T]hh:mm:ss.ssssss (time portion is optional)

  • datetime_offset: like datetime plus timezone

  • decimal, double, single: normal numeric representation for type

  • guid: hhhhhhhh-hhhh-hhhh-hhhh-hhhhhhhhhhhh (h is hex digit)

  • time: PnYnMnWnDTnHnMn[.m]S each component is optional

Commands

You can run commands in ILOData, or you can specify them on the command line, to run immediately on startup.

For example, command=get ~/T1 causes ILOData to exit immediately after executing the command.

Command Option Description
delete id_url Execute the given DELETE request.
exit Quit ILOData.
upload Upload queued requests.
get [request] Execute the given GET request.
help Print usage information.
patch id_url [prop=value] Execute the given PATCH request.

id_url is a URL identifying an entity.1
patch link_url [id_url] Execute the given request to modify a link.

link_url is a URL identifying a navigation property.1

id_url is n URL identifying an entity.1
post set_url [prop=value] Execute the given POST request.

set_url is a URL identifying an entity set.1

POST requests support:
  • Deep inserts and complex types. The syntax for both is the same. ILOData allows wrapping groups of properties for deep inserted entities or complex type properties in curly braces ({}).
  • Bind operations. To perform a bind, rather than setting the navigation property to an object ({}), you set the value to a URL.
put id_url [prop=value] Execute the given PUT request.

id_url is a URL identifying an entity.1
put link_url [id_url] Execute the given request to modify a link.

link_url is a URL identifying a navigation property.1

id_url is a URL identifying an entity.1
download Perform a delta download.
register name id_url Register a media stream request.

id_url is a URL identifying an entity.1
requestQueueIsEmpty Returns whether or not the request queue is empty.
set name=value Set an ILOData variable.
pagesize variable The page size for server-driven paging. pagesize must be a 32-bit integer greater than 0. A special value for this variable is NO_PAGING, which means do not use server-driven paging. NO_PAGING is the default.
unregister name Unregister a media stream request.
Batch operations The important commands for executing batch operations are:
  • start batch start a batch request.
  • start change set start a change set when within a batch.
  • add change set add the current change set to the batch
  • execute batch execute the requests in the current batch.
  • cancel batch cancel the current batch when building a batch.
  • cancel change set cancel the current change set when building a change set.
Each request within a change set is given an request number. The request number can be used as a Content-ID reference in later requests in the same change set.
send store [EncryptionKey] Send the offline store to SAP Mobile Services server for diagnostic purpose. If the EncryptionKey option is not specified, then this command will send a decrypted store to the server. If Encryptionkey is specified, then the send store command will send an encrypted store to the server with EncryptionKey. For example send store 123 will send the offline store with encryption key "123" to the server.
printDefiningQueries Print defining queries that have been added to the offline store.
printRequestQueue Print requests that are currently in the request queue.
undo id_url Undo pending changes made to an entity identified by id_url. For an existing entity downloaded from the back end, the entity will be restored to its original status. For a new entity created locally, the entity will be removed.

1 All URLs must be properly encoded, particularly spaces and slashes. All values that contain spaces or tabs must be quoted with double quotes ("). All other values can be, but aren't required to be, quoted with double quotes. Quoted values may contain \, \xhh and "" escape sequences. Except when noted, values are case insensitive.

Getting Option Values for SAP Mobile Services Server

You need to provide a set of mandatory option values in order to access the server, including host, port, .ini, service_root, custom_header and custom_cookie (for the tenant node cookie). You also need to know the ID of the application to access.

  • host: Consult your system administrator to get the host name. For example, a string like xxx-xxx.hanatrial.ondemand.com.
  • port: Usually 443.
  • enable_https: Always yes for SAP Business Technology Platform.
  • service_root: Knowing the ID of the application to use, it is a string, such as https://host/application_id.
  • custom_header: You must include X-SMP-APPID as a custom header in the form of custom_header=X-SMP-APPID:application_id.
  • custom_cookie: Follow the steps below to get the tenant node cookie.
    1. Log in to the SAP mobile service cockpit.
    2. Navigate to your application's details page, then switch to the "APIs" tab. Make a record of the URL for the "Server" API.
    3. Send a GET request using a REST client (such as Postman) to the URL you get in step #1 with a header X-SMP-APPID which value is the ID of the application.
    4. If the request is successful, the response will contain a cookie. Make a record of the cookie name and value. The cookie name and value will be used in the custom_cookie option in the form of cookie_name:cookie_value.

Examples

Create a New Offline Store Using the SAP Business Technology Platform Server

For the Neo platform, assuming your application requires no authentication (the application's Security Configuration is None) and the back-end OData service requires no authentication either (the application connectivity's SSO Mechanism is No Authentication):

The cookie value can be obtained by using the browser to make a request to the OData backend and using a browser such as Chrome or Firefox browser's debugger (F12 for Windows and Option + Command + I for MacOS to show). The access URL is like https://xxx-xxx.hanatrial.ondemand.com/{destination}?X-SMP-APPID={appid} cookie name is BIGipServer.... and X-SMP-SESSION.

Use the command below to access the server and open the offline store (replacing the xxx parts with your own values):

ILOData host=xxx-xxx.hanatrial.ondemand.com port=443 enable_https=yes service_root=https://xxx-xxx.hanatrial.ondemand.com/xxx defining_query=xxx custom_header=X-SMP-APPID:xxx  "custom_cookie=BIGipServer....:xxx" "custom_cookie=X-SMP-SESSION:xxx"

Another common use case is that your application requires basic authentication (the application's Security Configuration is Basic). In this case you need to specify the user name and password for accessing SAP Business Technology Platform:

ILOData ... username=xxx password=xxx

In the command above, represents other options as shown in the previous example and you also need to replace the xxx parts with your own values.

If your back-end OData service requires Basic Authentication, you need to configure the user name and password in the application's destination to let SAP Business Technology Platform know how to authenticate with the back end.

For the Cloud Foundry platform if, in general, your application uses OAuth authentication, follow these steps to get cookies and the OAuth token:

  • Get Server API URL in your application configuration, then build the URL {yourserverAPIulr}/mobileservices/accesstoken/current?auth=uaa. For example: https://mobile-xxx-yyy.cfapps.sap.hana.ondemand.com/mobileservices/accesstoken/current?auth=uaa
  • Using a browser such as Chrome or Firefox to access the above URL, then login in
  • Get the OAuth token, using the browser's debugger (F12 for Windows and Option + Command + I for MacOS to show)) to get the cookies. Cookie name is : JSESSIONID and __VCAP_ID__
  • Use the following command
   ilodata host=mobile-xx-xxxx.cfapps.sap.hana.ondemand.com port=443 enable_https=yes service_root=xxxx enable_debug_logging=yes custom_header="Authorization: Bearer {oauthToken}"  extra_stream_parms=skip_certificate_name_check=true custom_header="X-SMP-APPID:{appid}" defining_query=xxx custom_cookie="__VCAP_ID__:xxx" custom_cookie="JSESSIONID:xxx"

Create a New Entity

ILOData> post MySampleCollection strProp="myvalue1" dateProp="2015-01-01 T12:00:00"

Query the Error Archive

ILOData> get ErrorArchive

Batch Operations

  • Batch operation #1:

    ILOData > start batch
    -------------------------------------------------------------------------------------------------------
    Batch Operation #1 > start change set
    -------------------------------------------------------------------------------------------------------
    Change Set #1 Operation #1 > post Customers ID=3 GivenName="John" Surname="Doe"
    The operation was successfully added to the change set.
    ------------------------------------------------------------------------------------------------------
    Change Set #1 Operation #2 > post $1/Orders ID=3 FinancialCode="r1" Region="Eastern" SalesRepresentative=299 OrderDate="2017-07-04T00:00:00.000"
    The operation was successfully added to the change set.
    -------------------------------------------------------------------------------------------------------
    Change Set #1 Operation #3 > add change set
    The change set was successfully added to the batch.
    
  • Batch operation #2:

    Batch Operation #2 > get Customers?$expand=Orders&$orderby=ID
    The query was successfully added to the batch.
    
  • Batch operation #3:

    Batch Operation #3 > start change set
    -------------------------------------------------------------------------------------------------------
    Change Set #2 Operation #1 > post Customers ID=4 GivenName="John" Surname="Doe"
    The operation was successfully added to the change set.
    -------------------------------------------------------------------------------------------------------
    Change Set #2 Operation #2 > post $1/Orders ID=4 FinancialCode="r1" Region="Canada" SalesRepresentative=299 OrderDate="2017-07-04T12:00:00.000"
    The operation was successfully added to the change set.
    -------------------------------------------------------------------------------------------------------
    Change Set #2 Operation #3 > post $2/OrderItems LineID=1 ProductID=1 Quantity=1 ShipDate="2017-07-04T13:00:00.000"
    The operation was successfully added to the change set.
    -------------------------------------------------------------------------------------------------------
    Change Set #2 Operation #4 > add change set
    The change set was successfully added to the batch.
    
  • Batch operation #4:

    Batch Operation #4 > get Customers?$expand=Orders/OrderItems&$orderby=ID
    The query was successfully added to the batch.
    
  • Batch operation #5:

    Batch Operation #5 > execute batch
    

Deep Insert and Complex Type

  • This example performs a deep insert of a customer within a new order, then retrieves the new customer:

    ILOData > post Orders Customer={ID=2 GivenName="John" Surname="Doe"} ID=2 FinancialCode="r1" Region="Eastern" SalesRepresentative=299 OrderDate="2017-07-04T00:00:00.000"
    
    HTTP 201: Created
    -------------------------------------------------------------------------------------------------------------------
    ILOData > get Customers?$expand=Orders&$orderby=ID,Orders/ID
    
    HTTP 200: OK
    
  • This example illustrates how to work with complex types:

    ILOData > post Customers ID=3 GivenName="John" Surname="Doe" Address={Street="445 Wes Graham Way" City="Waterloo" Country="Canada"}
    
    HTTP 201: Created
    ------------------------------------------------------------------------------------------------------------------
    ILOData > upload
    
    Upload complete
    ------------------------------------------------------------------------------------------------------------------
    ILOData > download
    
    Download complete
    

Bind Operations

The following example creates a new customer and binds it to two existing orders:

ILOData > post Customers ID=3 GivenName="John" Surname="Doe" Orders="Orders(3)" Orders="Orders(4)"

HTTP 201: Created
------------------------------------------------------------------------------------------------------------------
ILOData > upload

Upload complete
------------------------------------------------------------------------------------------------------------------
ILOData > download

Download complete
------------------------------------------------------------------------------------------------------------------
ILOData > get Customers?$expand=Orders&$orderby=ID,Orders/ID

HTTP 200: OK

Tip

  • If you set the value of a navigation property to an object (for example, Orders= {….}) it is treated as a deep insert.
  • If you set the value of a navigation property to a URL (for example, Orders=Customers(101)), it is treated as a bind.

Event Log with Data Changed Flag

  1. From the ILOData command line open a store with the parameter:

    extra_stream_parms=_data_changed_by_download_flag;
    
  2. Retrieve the EventLog with the get EventLog command.

  3. If there have been any changes, or for the initial download, the log shows "DataChanged": true:

    {
        "_metadata": {
            "uri": "EventLog(1L)",
        "type": "offlineOData.Event"
        },
        "ID": "1",
        "Type": "download",
        "Time": "\/Date (1500988667440-240) \/",
        "Details": null,
        "DataChanged": true
    }
    

Note

Performing a download operation when no data has changed in the back end results in "DataChanged": false in the EventLog. DataChanged is null for other types of events, such as upload.

Interacting With Cloud Foundry

The ILOData command line utility can interact with mobile services applications on Cloud Foundry to perform local modifications, upload to the backend, and download from the backend, like other native Android or iOS offline OData applications. Below are the detailed steps to use ILOData to interact with the mobile services application on Cloud Foundry.

The first step is to get the application-related URL and parameters from the application's APIs table on the SAP mobile service cockpit. The following parameters are required:

  • server host: the host part of the mobile services server URL
  • service root: the backend destination name

For demonstration purposes, let's assume the server host is mobile-eu-integration-qa-sampleapp.cfapps.sap.hana.ondemand.com, the service root is com.sap.edm.sampleservice.v4, and the application ID is SampleApp.

Step 2: Fetch Application OAuth Token

The quickest way to get the token is to open the URL with a web browser: ${server host}/mobileservices/accesstoken/current?auth=uaa

Replace ${server host} with the host fetched in step 1, so the URL will be: https://mobile-eu-integration-qa-sampleapp.cfapps.sap.hana.ondemand.com/mobileservices/accesstoken/current?auth=uaa

You will get the OAuth token in the HTTP response after completing the login in the browser.

Step 3: Establish The HTTP Session

You need to fetch two mandatory cookies JSESSIONID and __VCAP_ID__ from the Server host and path /MobiLink/ServletAsync, using the OAuth token fetched above as the Authorization header.

Example curl command to establish the HTTP session:

curl  -v  -X POST 'https://mobile-eu-integration-qa-sampleapp.cfapps.sap.hana.ondemand.com/MobiLink/ServletAsync' \
--header 'ml-session-id-from-client: 4599e27a-1cf4-46c1-a6ba-000000000000' \
--header 'Authorization: Bearer eyJhbGciOiJSUzI1…’

< HTTP/2 204
< cache-control: no-cache, no-store, max-age=0, must-revalidate
< pragma: no-cache
< set-cookie: JSESSIONID=nSU2FsGBM***; Path=/; HttpOnly;Secure;SameSite=None
< set-cookie: __VCAP_ID__=0319e130-1e3f-4f0b-68d3-25af; Path=/; HttpOnly;Secure;SameSite=None

The cookies JSESSIONID and __VCAP_ID__ can be extracted from the set-cookie response headers.

Note

The ml-session-id-from-client is a so-called MobiLink session ID from the client. It is an internal header for the MobiLink protocol. The Offline OData client SDK uses this header to establish an HTTP session between the client device and the Offline OData server.

The Offline OData client first sends a bogus request to ${server host}/MobiLink/ServletAsync with the header ml-session-id-from-client and the value 4599e27a-1cf4-46c1-a6ba-000000000000.

The Offline OData server knows that a request with the header value 4599e27a-1cf4-46c1-a6ba-000000000000 is for session establishment and will do nothing but respond with an HTTP status code 204 immediately. When the Offline OData client gets the response, it extracts all cookies from the response headers and feeds the cookies to all subsequent synchronization requests.

If the HTTP status code is not 204 for this bogus request, it means the HTTP connection to the mobile services is probably not authenticated, and the user must login again to refresh the session.

Step 4 : Run ILOData

Now you can run ILOData with the below parameters:

host=${server host}
port=443
enable_https=yes
service_root=${service root}
custom_cookie="JSESSIONID:the_jessionidin_cookie_in_step3"
custom_cookie="__VCAP_ID__:the_vcap_id_cookie_in_step3"

Then add entity sets to be downloaded with the command “add defining query”, and type "download" to start the initial download. Below is an example:

ILOData host=mobile-eu-integration-qa-sampleapp.cfapps.sap.hana.ondemand.com port=443 enable_https=yes service_root=com.sap.edm.sampleservice.v4 custom_cookie="JSESSIONID:nSU2FsGBM***" custom_cookie="__VCAP_ID__:0319e130-1e3f-4f0b-68d3-25af"

ILOData > add defining query
> Enter the defining query name: Products
> Enter the defining query: Products
> Retrieve media streams (Y|N)?: N
The defining query was added successfully.

ILOData > add defining query
> Enter the defining query name: Stock
> Enter the defining query: Stock
> Retrieve media streams (Y|N)?: N
The defining query was added successfully.

ILOData > download
The store state has changed to 'Opening'
The store state has changed to 'Initializing'
The store state has changed to 'Initial communication'
The store state has changed to 'File downloading'
The store state has changed to 'Open'

ILOData > get Products/$count
HTTP 200: OK

150

ILOData >

Another thing about ILOData is that it accepts an input file to perform various tests, which can be used for automated synchronization tasks. For example, the following test.case file can be used as input for ILOData.

# the test.case file contains a series of commands to be executed by ILOData
% cat test.case
add defining query
Products
Products
N
download
get /Products/$count
exit

# run ILOData with the input file
ILOData host=mobile-eu-integration-qa-sampleapp.cfapps.sap.hana.ondemand.com port=443 enable_https=yes service_root=com.sap.edm.sampleservice.v4 custom_cookie="JSESSIONID:nSU2FsGBM***" custom_cookie="__VCAP_ID__:0319e130-1e3f-4f0b-68d3-25af" < test.case

Analyzing Request Queue

The ILOData provides a command printRequestQueue to print all the requests that are currently in the request queue. While the printRequestQueue command is useful, there are other commands that can be used to investigate the request queue.

Counting The Request Queue

Count the overall number of requests in the request queue:

ILOData > get RequestQueue/$count

Filtering The Request Queue

Filter the request queue by the request method:

ILOData > get RequestQueue?$filter=Method eq 'POST'

Filter the request queue by the request status:

ILOData > get RequestQueue?$filter=Status eq 'Unsent'
ILOData > get RequestQueue?$filter=Status eq 'Failed'

Filter the request queue by the request method and status:

ILOData > get RequestQueue?$filter=Method ne 'POST' and Status eq 'Successful'

Aggregating The Request Queue

Aggregate the request queue by the request method:

ILOData > get RequestQueue?$apply=groupby((Method), aggregate($count as Count))

Aggregate the request queue by the request status:

ILOData > get RequestQueue?$apply=groupby((Status), aggregate($count as Count))

Aggregate the request queue by the request method and status:

ILOData > get RequestQueue?$apply=groupby((Method, Status), aggregate($count as Count))

Last update: January 23, 2025