Skip to content

Setting Up an Application

Setting Up Server-Side Application

You must set up a server-side application on SAP Cloud Platform before an offline application that uses the Offline OData component on a client device can access the cloud platform and back end.

Note

A supported version of an OData (for example Version 2.0) service must be available and accessible by SAP Cloud Platform, for example Netweaver Gateway, Integration Gateway, SAP HANA, or a third-party service such as Apache Olingo.

  1. Determine your data requirements:

    • Identify the portions of your application that are to operate offline, and the OData entity sets to be referenced.
    • Compile the list of entity sets into an initial list of defining queries. Defining queries identify the entities and relationships that are required to operate offline.
    • Reduce the overall data transmitted to the client. Often an offline application requires only a subset of the data exposed through the OData service. Review each defining query to determine if you can reduce the amount of data by using OData query option $select, $filter, or other options. If so, add those OData URI primitives to the defining query.
    • Identify sharable defining queries. For security or performance reasons, an OData service may filter the results that are visible to the user who is logged in. In other cases, the result set may be the same for all application users. Such defining queries can be marked as shared on SAP Cloud Platform, which optimizes back-end requests. Review your defining queries and identify those that meet this "shared" definition.
  2. Create the application configuration initialization (.ini) file (only if non-default behaviors are needed) for the server-side application.

    An application configuration file consists of one or more endpoints, each of which can be optionally followed by one or more defining queries:

    • Endpoint – identifies and determines how the OData service from which the offline application retrieves and stores offline data is processed by SAP Cloud Platform: how the offline store is prepopulated before it is downloaded to the client, indexed, and so on. See Application Configuration File.
    • Defining Query – defines various characteristics of how the retrieved data for a given endpoint is managed by SAP Cloud Platform: whether the data is shared, refresh interval, delta tracking, and so on. See Defining Queries.
  3. On SAP Cloud Platform:

Both application code and configurations on the server side work together to achieve our needs.

Working with an Offline Store

Working with an offline store requires the following steps:

  1. Initialize and open an offline store. An offline application can manage multiple stores if required. When an offline store is opened for the first time, it's populated by an initial download, which is built into the open operation. For this step, the application must have network connectivity. The application communicates with the mobile services, which collect data from an OData service based on the defining queries, create the database that stores the data, and push that database to the offline store on the device.
  2. Perform any necessary operations (create, read, update, and delete) on the data in the offline store.
  3. Send pending modification requests to the mobile services (upload operation). For this step, the application must have network connectivity.
  4. Download data from the back end based on defining queries, and merge it into the offline store. The application must have network connectivity. See Synchronizing Data for more information.
  5. (Optional) Test the offline store. See Troubleshooting with ILOData

Note

The SAP Cloud Platform and the back end determine how conflict and error resolution are handled. See Handing Errors and Conflicts for more information.

Initialization of the Offline Store

An offline store is created for a given service URL and a set of defining queries. The service URL refers to an endpoint in the server-side application that has already been set up. There should only be one offline store per endpoint. You can have multiple offline stores per application.

The offline store is represented by an OfflineODataProvider class instance constructed using the service URL and a set of defining queries. The provider instance is passed to a service object that is either a DataService class instance, or in the case of proxy classes, an instance of a generated subclass extending DataService. The instance is used by the offline application to interact with the local OData service and is constructed using an OfflineODataProvider instance that it delegates to for executing requests against the offline store.

Note

When you see ... in the code samples in this guide, you need to replace with your own code to make it work.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// Retrieve the OkHttpClient initialized in Foundation module for 
// authentication which has the necessary information to communicate
// with the mobile services
OkHttpClient okHttpClient = ClientProvider.get();

// Initialize application context for use by OfflineODataProvider
AndroidSystem.setContext(applicationContext);

// Specify parameters for OfflineODataProvider construction
OfflineODataParameters parameters = new OfflineODataParameters();

// Use server driven paging with a page size of 100.
// Note that the "server" here refers to the offline store as a local service, not the back-end OData service
parameters.setPageSize(100);

// If you know that the back-end OData service or mobile services support repeatable requests, enable repeatable requests on client
// so Offline OData will generate repeatable request header for each request.
// If none of them support repeatable requests, you should disable this on the client.
// Be aware that disabling repeatable request can lead to multiple executions of the same requests in some cases
parameters.setEnableRepeatableRequests(true);

// Enable encryption. See next section for more details
parameters.setStoreEncryptionKey("...");

// Create a new instance of OfflineODataProvider
// with service URL from the mobile services
OfflineODataProvider offlineODataProvider =
    new OfflineODataProvider(new URL(SERVICE_URL), parameters, okHttpClient, null, null);

// Add defining queries
offlineODataProvider.addDefiningQuery(new OfflineODataDefiningQuery("req1", "/Events", false));
...

// Construct a DataService instance with the provider instance.
// This code uses the generated proxy class EventService which
// extends DataService.
EventService eventService = new EventService(offlineODataProvider);

See Authentication for more information about retrieving the OkHttpClient.

The provider needs to be closed if it is no longer needed:

1
2
3
4
5
6
7
// Close the provider
try {
    offlineODataProvider.close();
} catch ( OfflineODataException e ) {
    // Handle the error
    ...
}

Offline-specific operations, for example, upload and download, are not available from the DataService class. They can be accessed using OffineODataProvider. Due to potential long execution time, upload, download and open calls can be invoked only asynchronously. Internally, a new thread is created for processing, and the callback method onComplete(OfflineODataException error) in the listener interface is called upon completion. If the operation fails, an error parameter contains the detailed information.

Data Encryption with an Offline Store

Before deploying your app to production, you must enable offline store encryption to protect sensitive business or personal data (although you can omit data encryption for development and testing purpose).

By supplying an encryption key, the offline store is able to provide strong encryption, using the AES 256-bit algorithm, to provide security against skilled and determined attempts to gain access to the data. Encryption does come with a small impact on performance, the scope of the impact depending in part on the size of the cache.

When the offline store is opened for the first time, an encryption key must be supplied to enable encryption through the class OfflineODataParameters. Once the offline store is encrypted, the encryption key cannot be recovered from the offline store itself and must be presented on subsequent opens. It is critical that you securely store the key.

SAP recommends that you enforce a mobile passcode to protect a mobile app from unauthorized access. The mobile passcode derives an encryption key that secures a SecureKeyValueStore. The passcode is typically required during onboarding, subsequent application launches, and returning to foreground. Neither the passcode nor the derived encryption key for the SecureKeyValueStore is ever stored.

The encryption key for the offline store can be stored securely in the SecureKeyValueStore. Each time the offline store is opened, the SecureKeyValueStore must also be opened. See Foundation Module on SecureStore for more information.

An offline store is encrypted by an AES 256-bit cipher. The encryption key is used as an input to a password-based key derivation function. The length and complexity of the encryption key is critical to ensure brute force guessing attack will not succeed.

Consider the following when creating a key:

  • Do not include semicolons in your key.
  • Do not put the key itself in quotes, or the quotes are considered part of the key.
  • Lost or forgotten keys result in completely inaccessible stores.
1
2
3
4
5
6
// Set a strong random password of at least 16 characters
String strongEncryptionKey = ...;

// Assume that we have created and opened a secure key-value store 
// when user enters passcode
appSecureKeyValueStore.put("OfflineStoreEncryptionKey", strongEncryptionKey);

On application relaunch, the offline store encryption key can be retrieved as follows:

1
2
3
4
5
6
// Assume that we have opened the secure key-value store as user enters passcode
String strongEncryptionKey = appSecureKeyValueStore.getString("OfflineStoreEncryptionKey");
offlineODataParameters.setStoreEncryptionKey(strongEncyrptionKey);

// Open offline store
...

Multiuser Consideration

If the application is to support multiple users on the same device, information of the user providing the passcode can be obtained using foundation user API. See UserInfo documentation.

To avoid offline store files clashing between different users, you can use OfflineODataParameters to instruct OfflineODataProvider to store the database files under separate directories with user-specific names by leveraging information from UserInfo.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
// Get userId from UserInfo
String userId = ...;

// Get application directory
File filesFolder = appContext.getFilesDir();

// Create a user specific directory, applicable for initial open
File offlineStoreDir = new File(filesFolder, userId);
if (!offlineStoreDir.exists()) {
    offlineStoreDir.mkdir();
}

OfflineODataParameters offlineODataParameters = new OfflineODataParameters();
// Set user specific offline store name
offlineODataParameters.setStoreName("offlinestore-" + userId);
// Set user specific directory to store offline store files
offlineODataParameters.setStorePath(new URL("file:" + offlineStoreDir.toString()));
offlineODataParameters.setStoreEncryptionKey("...");

offlineODataProvider = new OfflineODataProvider(new URL(SERVICE_URL), offlineODataParameters, okHttpClient, null, null );

Another common approach is to remove the offline store before opening a new one for a different user.

Regardless of the approach, users should be mindful about uploading pending modification requests before releasing that device to a different user.

Certificate-Based Authentication

If the offline application is configured to use certificate-based authentication, OfflineODataProvider requires access to the certificate to establish a SSL session with the server. This is accomplished by providing an instance of the SslClientAuth class as the fourth parameter to the constructor. See SSL Client Certificate documentation.