7 Using Oracle Mobile Cloud Service Platform APIs in a MAF Application

This chapter describes how to use Oracle Mobile Cloud Service (MCS) platform APIs, such as the Storage API, update user information, plus send analytics and diagnostic events to MCS for analysis by the Analytics and Diagnostic features of MCS.

This chapter includes the following sections:

7.1 Introduction to Using Oracle Mobile Cloud Service Platform APIs

MAF provides support for MCS platform APIs such as the Storage API and the Analytics API. MAF also supports updating MCS user information and sending diagnostic information to MCS Diagnostics.

The first step to use any of these platform APIs is to connect your MAF application to MCS. To do this, you select the MCS Connection checkbox and specify the mobile backend ID and anonymous access key when you connect to the service to create the client data model. For more information about this task, see Connecting to a REST Service to Create the Client Data Model. Once you complete the wizard where you specify the connection details, JDeveloper writes the following entries to your application’s mobile-persistence-config.properties file:

# MCS connection details, applicationScope EL Expressions 
# are allowed for backend ID and anonymous key
mcs.connection=MCS
mcs.mobile-backend-id=bcda8418-8c23-4d92-b656-9299d691e120
mcs.anonymous-key=c3RldmVuLmtpbmc6U3RhYWYyMDE1IQ==

The access key creates the Authorization header when your application accesses an MCS platform API or custom API before you have authenticated with MCS. After you authenticate against MCS, MAF automatically injects the Authorization header into every REST call based on the user login credentials. That is, it ignores the anonymous access key that is specified in the mobile-persistence-config.properties file. For more information about this key, see What You May Need to Know About the MCS Anonymous Access Key.

The oracle.maf.api.cdm.persistence.manager.MCSPersistenceManager class handles all calls to MCS platform APIs. By default it uses the settings in the mobile-persistence-config.properties file, but you can override the settings in this file by calling the following methods in custom code:

  • setConnectionName(String connectionName)

  • setMobileBackendId(String mobileBackendId)

  • setAnonymousKey(String anonymousKey)

  • setAuthHeader(String authHeader)

  • login(String userName, String password)

If your application needs to support dynamic MCS connections, you can specify an EL expression for the mcs.mobile-backend-id and mcs.anonymous-key values in the mobile-persistence-config.properties file. The actual URL of the MCS connection can be changed at runtime using the AdfmfJavaUtilities.overrideConnectionProperty method. The following sample code demonstrates how to dynamically set the MCS connection URL based on a user preference:

String mcsHost = (String)AdfmfJavaUtilities.evaluateELExpression("#{preferenceScope.application.connection.host}");
AdfmfJavaUtilities.clearSecurityConfigOverrides("MCS");
AdfmfJavaUtilities.overrideConnectionProperty("MCS", "restconnection", "url", mcsHost+"/mobile");

You typically include this code in your application lifecycle listener start() method. For more information, see Using Lifecycle Listeners in MAF Applications. For more information about using the AdfmfJavaUtilities.overrideConnectionProperty method, see How to Update Connection Attributes of a Named Connection at Runtime.

7.2 Accessing Oracle Mobile Cloud Service User Information

MAF’s MCSPersistenceManager provides methods to log users in and out of MCS plus retrieve and update user information.

Use the following method to do a programmatic login against MCS:

String response = new MCSPersistenceManager().login(userName, password);

This returns the response payload from MCS. Although we describe this method for your information, we recommend that you use MAF’s declarative support for user authentication. The latter option allows you to use basic authentication or OAuth. It also provides options to remember the user name and password in a secure way. In addition, by securing a MAF feature the login screen automatically appears when you access a secured feature for the first time.

Use the following method to do a programmatic logout from MCS:

String response = new MCSPersistenceManager().logout();

Again, we recommend you use MAF’s declarative support for securing your application. When you use MAF’s declarative support, you should use the following MAF API call to log out:

AdfmfJavaUtilities.logout();

For more information about MAF’s declarative support for user authentication, see Securing MAF Applications.

Use the following method to retrieve all the attributes of a user stored in MCS:

String response = new MCSPersistenceManager().findUser(userName);

It returns a response payload like this:

{
  "id": "a8acf41f-50a7-473e-8376-d6346cf188be",
  "username": "GEVANS",
  "email": "george.evans@ebsss.com",
  "firstName": "George",
  "lastName": "Evans",
  "jobTitle": "Cloud Solutions Architect",
  "links": [
    {
      "rel": "canonical",
      "href": "/mobile/platform/users/GEVANS"
    },
    {
      "rel": "self",
      "href": "/mobile/platform/users/GEVANS"
    }
  ]
}

Note:

Custom attributes that you defined for your user realm in MCS are also returned. In the above example, jobTitle is an example of a custom attribute.

Update user attributes with the following method:

String response = new MCSPersistenceManager().updateUser(userName, payload);

The payload you pass is a list of the attributes you want to change, for example:

{
  "email" : "gevans@ebsss.com",    
  "jobTitle": "Senior Cloud Solutions Architect"
}

7.3 Accessing Files in an Oracle Mobile Cloud Service Storage Collection

Describes how to retrieve, download and filter storage objects from an MCS storage collection in a MAF application.

MAF provides both programmatic and declarative access to the MCS Storage API. To use declarative access, you create a bean data control from oracle.maf.api.cdm.mcs.storage.StorageObjectService. The generated data control provides a range of operations to access files in a storage collection. You can also access the StorageObjectService class programmatically. In addition to retrieving files, you can create and update files in an MCS storage collection and you can associate MCS storage files with your data objects. For example, you could create a list page showing employees with a small thumbnail employee image coming from MCS that navigates to an employee form page with the picture that an end user can update by taking a new picture. Offline support is fully implemented as the storage object metadata and file are stored on the device. Storage objects can be created and updated in offline mode. MAF synchronizes these objects with MCS once the device returns online, as described in Synchronizing Offline Transactions from a MAF Application.

For more information about implementing the just-described use cases, see:

7.3.1 How to Create the StorageObjectService Bean Data Control

Describes how to create and use the StorageObjectService data control that you can use to retrieve and manage objects in an MCS storage collection.

To create the StorageObjectService bean data control:
  1. In the Applications window, select any existing project and in the main menu, choose File and then New > From Gallery.
  2. In the New Gallery, expand Business Tier, select Data Controls and then Bean Data Control, and click OK.
  3. In the Choose Bean Class dialog, click the search icon to display the Class Browser dialog and enter StorageObjectService in the Match Class Name input field.
    JDeveloper retrieves the oracle.maf.api.cdm.mcs.storage.StorageObjectService class, as shown in Figure 7-1.

    Figure 7-1 StorageObjectService in Class Browser

    View of the Class Browser dialog that has been invoked from the Create Bean Data Control dialog and where a user has entered StorageObjectService in the Match Class Name input field.
  4. Click OK.
    JDeveloper specifies StorageObjectService as the name for the data control in the Choose Bean Class dialog.
  5. Click Next and Finish.

7.3.2 What Happens When You Create the StorageObject Bean Data Control

JDeveloper generates the StorageObjectService data control in the Data Controls panel.

Figure 7-2 Generated StorageObjectService Data Control

View of the Data Controls panel with a generated StorageObjectService selected.

You typically use the methods that this data control exposes to download and display a collection of files or a single file. The REST calls that your MAF application makes to MCS to retrieve storage objects execute in the background when the StorageObject mapping descriptor’s remoteReadInBackground property is set to true, as shown in the following example from the persistence-mapping.xml file. Similarly, REST calls from your MAF application to create or update storage objects in MCS execute in the background when the remoteWriteInBackground property is set to true.

You cannot modify these properties using the Edit Persistence Mappings wizard. Instead, you edit these properties directly in the persistence-mapping.xml file that is in the ApplicationController\src\META-INF directory of your application’s workspace.

   <classMappingDescriptor className="oracle.maf.api.cdm.mcs.storage.StorageObject" persisted="true">
      <crudServiceClass className="oracle.maf.api.cdm.mcs.storage.StorageObjectService" autoIncrementPrimaryKey="true" 
                        localPersistenceManager="oracle.maf.api.cdm.persistence.manager.DBPersistenceManager" 
                        remotePersistenceManager="oracle.maf.impl.cdm.persistence.manager.MCSStoragePersistenceManager" 
                        remoteReadInBackground="true" 
                        remoteWriteInBackground="true" 
                        showWebServiceInvocationErrors="true" 
                        autoQuery="false" 
                        enableOfflineTransactions="true"/>
      <table name="STORAGE_OBJECT">
         <primaryKeyColumn name="ID"/>
         <primaryKeyColumn name="COLLECTION_NAME"/>
      </table>
      <attributeMappings>

7.3.3 How to Retrieve All Files from an Oracle Mobile Cloud Service Storage Collection

Describes how to use the method that the StorageObjectService data control exposes to retrieve all storage objects from MCS.

Figure 7-3 illustrates how the findAllStorageObjectsInCollection(String) method from the StorageObjectService data control can be used to render thumbnail images for each employee in a collection of employees. Drag and drop this method to an AMX page and choose MAF Parameter Form in the context menu that appears. This creates an input field for the collection name and a button to execute the method. After the method executes, the data control’s storageObjects collection populates with the content of the collection. You can then drag and drop the storageObjects collection onto your AMX page as, for example, a MAF List View.

Figure 7-3 Image Files from an MCS Storage Collection in a List View

The surrounding text describes the image.

Alternatively, you can drag and drop the findAllStorageObjectsInCollection method as a method activity onto a task flow and then add a control flow rule from this activity to your AMX page that shows the content of the collection. When you perform this drag and drop, you need to provide a value for the collectionName parameter. The value you provide can be hardcoded or an EL expression.

The storageObjects collection is initially populated with the local storage objects from the SQLite database, the call to MCS executes in the background, and the UI refreshes once the MCS results return.

Figure 7-3 shows how the storage file is used to display the image in each list item. To accomplish this, set the source attribute of the amx:image element to the value of the storage object’s filePath attribute. The filePath attribute holds the reference to the downloaded file on the mobile device.

<amx:image source="#{row.filePath}" inlineStyle="width:40px;height:40px;"  id="i1"/>

The findAllStorageObjectsInCollection method returns the metadata of each storage object. In order to download the image files, drag and drop the storage object’s downloadIfNeededInBackground attribute as an outputText element. At runtime, the MAF application calls the getDownloadIfNeedInBackground method on the StorageObject class. This method returns an empty string and triggers the download of the image files. If the file has been downloaded before, MAF first makes a HEAD call to check if the storage object’s ETag in MCS has the same value as the local ETag stored in the SQLite database. If the values differ, MAF downloads the file. The following sample shows the listView component’s code that renders the list view shown in Figure 7-3.

If you do not want the page to load before MAF downloads all image files, drag and drop the downloadIfNeeded attribute instead.

<amx:listView var="row" value="#{bindings.storageObjects.collectionModel}"
              fetchSize="#{bindings.storageObjects.rangeSize}"
              selectedRowKeys="#{bindings.storageObjects.collectionModel.selectedRow}"
              initialScrollRowKeys="#{bindings.storageObjects.collectionModel.selectedRow}"
              selectionListener="#{bindings.storageObjects.collectionModel.makeCurrent}"
              showMoreStrategy="autoScroll" bufferStrategy="viewport" id="lv1">
  <amx:listItem id="li1" rendered="#{row.contentType=='image/jpeg'}">
    <amx:tableLayout width="100%" id="tl1">
      <amx:rowLayout id="rl2">
        <amx:cellFormat width="40px" halign="center" rowSpan="2" id="cf6">
          <amx:image source="#{row.filePath}" inlineStyle="width:40px;height:40px;" id="i1"/>
          <amx:outputText value="#{row.downloadIfNeededInBackground}" id="ot6"/>
        </amx:cellFormat>
        <amx:cellFormat width="60%" height="#{deviceScope.device.os=='Android'?'36':'32'}px" id="cf4">
          <amx:outputText value="#{row.name}" id="ot4"/>
        </amx:cellFormat>
        <amx:cellFormat width="10px" rowSpan="2" id="cf3"/>
        <amx:cellFormat width="40%" halign="end" id="cf5">
          <amx:outputText value="#{row.contentType}" styleClass="adfmf-listItem-highlightText" id="ot5"/>
        </amx:cellFormat>
      </amx:rowLayout>
      <amx:rowLayout id="rl1">
        <amx:cellFormat width="60%" height="#{deviceScope.device.os=='Android'?'22':'19'}px" id="cf1">
          <amx:outputText value="#{row.createdOn}" styleClass="adfmf-listItem-captionText" id="ot2"/>
        </amx:cellFormat>
        <amx:cellFormat width="40%" halign="end" id="cf2">
          <amx:outputText value="#{row.ETag}" styleClass="adfmf-listItem-captionText" id="ot3"/>
        </amx:cellFormat>
      </amx:rowLayout>
    </amx:tableLayout>
  </amx:listItem>
</amx:listView>

7.3.4 How to Filter the List of Storage Objects from an MCS Storage Collection

Describes how to filter the list of storage objects that the StorageObjectService retrieves from an MCS storage collection.

The StorageObjectService API supports the retrieval of all objects in an MCS storage collection or just one object. You can filter the results on the device by using the SQLite database that stores all the storage object metadata. To do this, create a subclass that extends from oracle.maf.api.cdm.mcs.storage.StorageObjectService. Write code in this subclass that filters the retrieved storage objects like the code you write in the service object classes to filter data objects in your MAF application, as described in Using Filtered Entity Lists.

Generate a data control from the subclass you created that extends from oracle.maf.api.cdm.mcs.storage.StorageObjectService.

7.3.5 How to Retrieve a Single File from an Oracle Mobile Cloud Service Storage Collection

Describes how to use the findStorageObject method that the StorageObjectService data control exposes to retrieve and download a single storage object from MCS.

The findStorageObject method takes the following two parameters:

  • Name of the collection in MCS

  • ID of the storage object

Drag and drop the findStorageObject method as a MAF Parameter Form to create input fields for the two parameters and a button to execute the method. You can drag and drop the result StorageObjects element of this method onto your AMX page as, for example a MAF Read-Only Form.

If the storage object that you download is an image, drag and drop the filePath attribute as an outputText component and then change it into an image component, as shown in the following example:

<amx:image id="i1" source="#{bindings.filePath.inputValue}" inlineStyle="height:200px;"/>

Figure 7-4 shows an AMX page where the findStorageObject method downloaded an image file to render in the UI of the MAF application.

Figure 7-4 findStorageObject Method Downloading an Image File

The center of the image shows a list view page from a MAF application that renders a picture. To the left of the picture is a view of the data control that accesses the storage object. The right shows the filePath method attribute that retrieves the image file that renders the picture.

7.3.6 How to Associate Storage Objects with Data in your MAF Application

Describes how to write Java code that uses the StorageObjectService and StorageObject classes to retrieve a storage object from an MCS storage collection and associate it with a data object in your MAF application.

Enriching existing data from a backend system of record with data that can be captured using a MAF application’s device capabilities, such as a camera, is a frequent requirement when building MAF applications. The following code samples describe how you can associate employee data with a picture that is retrieved from an MCS storage collection.

You can use the default constructor to create an instance of the StorageObjectService class or, alternatively, the constructor that enables you to override the values for remoteReadInBackGround and remoteWriteInBackground set in your application’s persistence-mapping.xml file. This is easier in Java code so you directly work with the results of the REST calls.

The following code example demonstrates how to get all storage objects in an MCS storage collection using the latter constructor:

StorageObjectService sos = new StorageObjectService(false,false);
sos.findAllStorageObjectsInCollection("HR");
List<StorageObject> storageObjects = sos.getStorageObjects();

The following code samples show the Java code to add an employee’s picture to an employee list view and form page where the employee data comes from a custom MCS API and an MCS storage collection (“HR”) stores the picture(s). The code sample also demonstrates how to add a picture for a new employee using the device’s camera.

We use the following naming convention for employee pictures to associate pictures with the correct employee(s):

EmpImg[EmployeeID suffix]  
// Example: EmpImg100

First, add an image property of type StorageObject to the Employee data object, with the following getter and setters methods:

private StorageObject picture;

public StorageObject getPicture() {
  if (picture == null) {
    StorageObjectService sos = new StorageObjectService(false, false);
    picture = sos.findOrCreateStorageObject("HR", "EmpImg" + getId());
    picture.setDownloadCallback(() -> {
        refreshUI(Arrays.asList(new String[]{"picture"}));
      });
    picture.getDownloadIfNeededInBackground();
  }
  return picture;
}

public void setPicture(StorageObject picture) {
  this.picture = picture;
}

In the getter method we check whether the picture StorageObject is still null. If so, we instantiate the StorageObjectService and then call the findOrCreateStorageObject method where we pass in the name of the MCS collection and the ID of the employee picture file in the MCS collection. This method queries the local on-device database and returns the storage object if there is a matching row in the STORAGE_OBJECT table. If no row is found, a new StorageObject instance is created with the collection name and object ID set. We then specify a callback (setDownloadCallback(() ->) that allows us to refresh the UI once the employee picture downloads. Finally, we call the getDownloadIfNeededInBackground method which calls MCS to download the file and update the storage object metadata if needed.

Note:

If the file has been downloaded before, MAF first makes a HEAD call to check whether the ETag of the storage object in MCS has the same value as the local ETag value stored in the SQLite database. If the value has changed, the file will be downloaded.

The download location of the file on the device is a subdirectory named /MCS/[collectionName] under the application directory (the value returned by AdfmfJavaUtilities.getDirectoryPathRoot(AdfmfJavaUtilities.ApplicationDirectory)). The name of the file is the name of the storage object in MCS, or the ID if the name is not set in MCS. You can override the default download directory by calling the StorageObject’s setDirectoryPath(String directoryPath) method.

Now that you have added the picture StorageObject to the Employee class, you can drag and drop the picture filePath property to add an image to the list of employees within a department, as illustrated in Figure 7-5.

Figure 7-5 Adding an Image to a ListView

Shows a view of the Data Controls panel with a filePath property that, when dragged and dropped to an AMX page, renders an image file in the AMX page.

When you navigate to the Department page for the first time, you will see a slight delay before the employee pictures appear. For each employee in the department a parallel REST call is made in the background to fetch the picture from MCS. The next time you run the application, the images appear immediately.

By default, an error message appears when an employee does not yet have a picture in MCS because the REST call results in a 404 Not Found response, as shown in Figure 7-6.

Figure 7-6 Web Service Error Message

An error message that appears to end users when the web service that the application attempts to connect to is unavailable.

To avoid end users seeing web service invocation error messages, such as that shown in Figure 7-6, set the showWebServiceInvocationErrors property in the persistence-mapping.xml to false for the StorageObject descriptor, as shown by the following example.

<classMappingDescriptor className="oracle.maf.api.cdm.mcs.storage.StorageObject" persisted="true">
  <crudServiceClass className="oracle.maf.api.cdm.mcs.storage.StorageObjectService"
             autoIncrementPrimaryKey="true"
             localPersistenceManager="oracle.maf.api.cdm.persistence.manager.DBPersistenceManager"
             remotePersistenceManager="oracle.maf.impl.cdm.persistence.manager.MCSStoragePersistenceManager"
             remoteReadInBackground="true" remoteWriteInBackground="true" 
             showWebServiceInvocationErrors="false"
             autoQuery="false" enableOfflineTransactions="true"/>
                  

To upload a new employee picture, add a commandLink component to the employee form page that calls a managed bean method that takes a picture using the camera or selects an existing picture from the picture gallery. The following example demonstrates a possible implementation of such a managed bean method:

public void takePicture(ActionEvent actionEvent) {
  DeviceManager device = DeviceManagerFactory.getDeviceManager();
  int cameraType = 
    device.hasCamera() ? DeviceManager.CAMERA_SOURCETYPE_CAMERA: DeviceManager.CAMERA_SOURCETYPE_PHOTOLIBRARY;
  String picture = device.getPicture(50, DeviceManager.CAMERA_DESTINATIONTYPE_FILE_URI, cameraType, false,
                          DeviceManager.CAMERA_ENCODINGTYPE_PNG, 520, 380);
  Employee emp =
    (Employee) AdfmfJavaUtilities.getELValue("#{bindings.employeesIterator.currentRow.dataProvider}");
  // the camera returns an URI starting with file://, we remove this prefix to get proper file path
  emp.addPicture(picture.substring(7));
}

After taking the picture, obtain the current instance of employee and call the addPicture method on this instance, as demonstrated in the following example:

public void addPicture(String tempFilePath) {
  StorageObjectService sos = new StorageObjectService();
  StorageObject so = sos.findOrCreateStorageObject("HR", "EmpImg" + getId());
  so.setContentType("image/png");
  try {
    so.setContentStream(new FileInputStream(tempFilePath));
    sos.saveStorageObject(so);
    setPicture(so);
    // remove picture from temporary camera directory
    new File(tempFilePath).delete();
  }
  catch (FileNotFoundException e) {
    sLog.severe("Employee picture file not found");
  }
}

Similar to the getPicture method, addPicture first gets a StorageObject instance by calling the findOrCreateStorageObject method. We then set the contentType and contentStream and call the saveStorageObject method on the StorageObjectService. This method saves the storage object metadata in the SQLite database, streams the picture to the file system and calls MCS to add the file to the HR MCS storage collection. After the file is saved, you can remove the temporary file created by the device camera to free up disk space.

Note:

Instead of having the camera return a file URI, you can also obtain the image as a base64 encoded string by using DeviceManager.CAMERA_DESTINATIONTYPE_DATA_URL as the value for destinationType. This is not recommended because it loads the entire photo into memory and can easily crash your mobile application with an OutOfMemory error when taking high quality photos.

To remove an employee picture, add a removePicture method to the Employee class and drag and drop this method onto the Employee form page as a button or link.

public void removePicture() {
  StorageObjectService sos = new StorageObjectService();
  StorageObject so = sos.findOrCreateStorageObject("HR", "EmpImg" + getId());
  sos.removeStorageObject(so);
  // get new "empty" storageObject and refresh UI       
  so = sos.findOrCreateStorageObject("HR", "EmpImg" + getId());
  setPicture(so);
  refreshUI(Arrays.asList(new String[]{"picture"}));
}

The removeStorageObject method deletes the storage object row in the SQLite database, removes the file from the file system and calls MCS to remove the file from the MCS collection.

For more information about the StorageObject and StorageObjectService, see the Java API Reference for Oracle Mobile Application Framework.

7.4 Sending Analytics Information to Oracle Mobile Cloud Service

A MAF application with one or more mobile backends (MBE) hosted on MCS can send analytics information about application usage to MCS Analytics.

Analytics information that MAF generates to send to MCS provides information about the application lifecycle and an end user’s interaction with the MAF application. MAF categorizes analytics events into MAF Framework events and business events. You send information captured in response to MAF Framework events to MCS by configuring properties in your application’s logging.properties file. Examples of MAF framework events includes application start, feature events like activate and feature navigation, and user authentication events. MAF logs these events by default when you configure your application to send analytics information to MCS.

The following example shows the payload that MAF generates and transfers to MCS for a FeatureNavigation MAF framework event that occurs when an end user is redirected to a login application feature (LF1) before navigating to secured application features (secure-feature-1, and secure-feature-2). MAF logs all MAF framework events within a session that starts when a MAF application that is configured to send analytics information activates. The session ends when the MAF application deactivates. In the following example, the sessionID property identifies the session.

JDeveloper’s Log window displays this payload when you deploy your MAF application to a device in debug mode.

"name":"FeatureNavigation", "properties":{"SourceId":"null","DestinationId":"LF1"}, "type":"custom", "timestamp":"2015-11-06T20:35:27.384Z", 
                                                    "sessionID":"com.company.MafAnalytics_736ad3d4-3443-4f65-8378-4e653ade2d30_160121114922"

"name":"FeatureNavigation", "properties":{"SourceId":"LF1","DestinationId":"secure-feature-1"}, "type":"custom", 
             "timestamp":"2015-11-06T20:35:27.384Z", "sessionID":"com.company.MafAnalytics_736ad3d4-3443-4f65-8378-4e653ade2d30_160121114922"

"name":"FeatureNavigation", "properties":{"SourceId":"secure-feature-1","DestinationId":"secure-feature-2"}, "type":"custom", 
              "timestamp":"2015-11-06T20:35:27.384Z", "sessionID":"com.company.MafAnalytics_736ad3d4-3443-4f65-8378-4e653ade2d30_160121114922"

Business events are events that you (the application developer) define in your application. You capture the analytics information for the event using the APIs that MAF provides in oracle.maf.api.analytics.AnalyticsUtilities. MAF also exposes a data control method (fireEventListener) on the ApplicationFeatures data control. Drag and drop this data control method to an AMX page where you can configure it to listen for the event that you define. These APIs can also be used to send analytics from MAF Framework events to MCS.

MAF also enables you to send context event information (device model, country, time zone, and so on) to MCS or to another repository that you choose.

You can also send analytics to repositories other than MCS.

For more information, see:

7.4.1 How to Configure the Transfer of Analytics to Oracle Mobile Cloud Service

MAF populates the logging.properties file in a new MAF application with the properties that you need to configure to send analytics information from your MAF application to MCS Analytics.

The following example shows the ready-to-use entries that the logging.properties file contains when you create a new MAF application.

# Configure the analytics logger
# Analytics events are logged only if oracle.maf.api.analytics.level=ALL
# Set to OFF or any level other than ALL to disable analytics
oracle.maf.api.analytics.level=ALL
oracle.maf.api.analytics.handlers=oracle.maf.api.analytics.LoggerAnalyticsHandler, oracle.maf.api.analytics.McsAnalyticsHandler
oracle.maf.api.analytics.custom.level=INFO
oracle.maf.api.analytics.LoggerAnalyticsHandler.level=INFO

# Configure MCSHandler
oracle.maf.api.analytics.McsAnalyticsHandler.level=INFO
oracle.maf.api.analytics.McsAnalyticsHandler.connectionId=Mcs_Connection_Id
oracle.maf.api.analytics.McsAnalyticsHandler.batchSize=25
oracle.maf.api.analytics.McsAnalyticsHandler.offlineWrite=false
oracle.maf.api.analytics.McsAnalyticsHandler.recordUsername=false
oracle.maf.api.analytics.McsAnalyticsHandler.contextProviderClassName=oracle.maf.api.analytics.McsContextProvider

Of the properties listed in the previous example, only connectionId is mandatory. Table 7-1 describes the optional properties. The connectionId property is where you define a valid connection to a MCS MBE. Use the connectionId defined in your application’s connection.xml as the value for this property. If you do not specify a valid connectionId, MAF does not send events to MCS. Instead, MAF appends these events on disk until the maximum number of events allowed by the device is reached.

A valid connectionId in your application’s connection.xml file uses the ID of the MBE (oracle-mobile-backend-id) and the application key (oracle-mobile-application-key) that is generated when the MAF application registers with the MBE. MAF adds these two values to the HTTP header that creates a connection to MCS. These values identify the MBE to which the MAF application sends analytic events and identify the application from which the analytics events originate.

Note:

You do not need to register your MAF application as a client on MCS for all 3 platforms (Android, iOS, and Universal Windows Platform). Register the MAF application for one platform and use the generated application key as a value for oracle-mobile-application-key.

If the connection to the MCS MBE uses HTTP basic authentication type, associate oracle/wss_http_token_client_policy with the connection. For connections that use Oauth, associate oracle/http_oauth2_token_mobile_client_policy with the connection. If you do not associate the correct policy with the connection, MAF does not flush analytics events to MCS. For more information about associating a security policy with a connection, see Accessing Secure Web Services.

The following example shows extracts of an application connections.xml file with values for the properties just discussed.

<References xmlns="http://xmlns.oracle.com/adf/jndi">
  <Reference name="Mcs_Connection_Id" className="oracle.adf.model.connection.url.HttpURLConnection"
             adfCredentialStoreKey="McsLoginConn" xmlns="">
    ...
    <RefAddresses>
      <XmlRefAddr addrType="Mcs_Connection_Id">
        <Contents>
          <urlconnection name="Mcs_Connection_Id" url="http://mcs_instance.oracle.com:7201"/>
    ...
  </Reference>
  <Reference name="McsLoginConn" className="oracle.adf.model.connection.adfmf.LoginConnection"
             adfCredentialStoreKey="McsLoginConn" partial="false" manageInOracleEnterpriseManager="true"
             deployable="true" xmlns="">
    <Factory className="oracle.adf.model.connection.adfmf.LoginConnectionFactory"/>
    <RefAddresses>
      <XmlRefAddr addrType="adfmfLogin">
        <Contents>
          <login url="http://mcs_instance.oracle.com:7201/mobile/platform/users/login"/>
          <logout url="http://mcs_instance.oracle.com:7201/mobile/platform/users/logout"/>
          <customAuthHeaders>
            <header name="oracle-mobile-backend-id" value="0e4a9dfa-046a-4aaa-b8dd-331044ad81f4"/>
            <header name="oracle-mobile-application-key" value="be53201a-8674-48d7-96d0-bb02f4cd06c5"/>
          </customAuthHeaders> 
    ...
</References>

Configure the following optional entries in the logging.properties file to implement the described functionality.

Table 7-1 Optional Properties to Manage the Transfer of Analytics from a MAF Application

Property Description
batchSize An optional property that determines the number of events saved locally before the MAF application sends them to an associated MCS instance. Events will be uploaded in batches to MCS. There is a limit on maximum batch size (65). If batchSize is not provided or exceeds the maximum limit of 65, a default batchSize of 25 applies.
offlineWrite An optional property that determines if offline buffering of events should be enabled or not. It is possible that events get generated while a device is offline or the client is not able to establish a connection with MCS. Configure the offlineWrite property if you do not want to lose these events. Once the connection is re-established, MAF flushes these saved events to MCS. The offlineWrite property ensures that those events get cached to disk to enable their buffering while offline. MAF provides support for up to 250 events. Events will be saved on a rolling basis. That is, MAF saves the last 250 events. This ensures offline buffering of the latest events. MAF also flushes events to MCS when the application deactivates, irrespective of whether batchSize has been reached or not. By default, offlineWrite is disabled. Set it to True to enable it.
recordUsername An optional property that determines if username should be captured or not. Set to True so that MAF captures the username when a user logs into a secured feature. If an application does not contain any secured feature or if the user has not yet logged into the secured feature then username remains null. If the application contains several secured features, then username will be updated based upon the credentials (username) used to log into that feature. The username captured will be sent as one of the fields in the context event. Therefore, if you intend to capture username, configure logging.properties so that the recordUsername property is True and contextProviderClassName has a valid class name for generating the context event. For example, oracle.maf.api.analytics.McsAnalyticsHandler.recordUsername=true .
contextProviderClassName Optional. Provide a value for this property when the context event is generated. The value you specify determines the class that generates the context event for MCS. The context event contains information like timezone offset, geolocation and device information. It can also contain username if recordUsername is set to True. The contextProviderClassName property is disabled by default. The following example shows how you enable it: oracle.maf.api.analytics.McsAnalyticsHandler.contextProviderClassName=oracle.maf.api.analytics.McsContextProvider

7.4.2 How to Programmatically Send Analytics to Oracle Mobile Cloud Service

MAF provides an API in oracle.maf.api.analytics.AnalyticsUtilities that you can use to send events to MCS.

The AnalyticsUtilities class provides the following API:

public static void fireEvent(Level level, String category, String eventName)        
// Send an event without a payload

public static void fireEvent(Level level, String category, String eventName, JSONObject payload)  
// Send an event with a JSON payload      


level:     The logging level of the event. Set to any standard level supported by Java logging. 

category:  The category of the event. Set to 'custom' for all events except context, 
           sessionStart(MAF Framework event) and sessionEnd(MAF Framework event). Set the latter events to 'system'.

eventName: Provide your own event name if you do not use an event provided in the AnalyticsUtilities class. 
           Throws an exception if null.

payload:   A JSONObject that contains key-value pairs for custom events. The JSONObject must be of type String. 
           No other data type is supported.

                         

Configure your application’s logging.properties file with the values necessary to transfer analytics to MCS before you use this API. Specify, for example, the MCS connectionID.

The following example demonstrates how to use this API to send analytics from a MAF application to MCS.

     
//  Sending events from your application. 

        //  The following logs event when there is no payload to register for an event.
        AnalyticsUtilities.fireEvent(Level.WARNING, AnalyticsUtilities.CATEGORY_CUSTOM, "EVENT_VIDEO_ACTIONS");

        //  The following logs event when there is a JSON payload to send for a custom event.
        try
                {
                  JSONObject   payload = new JSONObject(); 
                  payload.put("PAYLOAD_VIDEONAME", getFileName());
                  payload.put("PAYLOAD_ACTION", getAction());      
                  // Creating a custom event 'EVENT_VIDEO_ACTIONS' of level INFO
                  AnalyticsUtilities.fireEvent(Level.INFO, AnalyticsUtilities.CATEGORY_CUSTOM, "EVENT_VIDEO_ACTIONS" , payload);
                } 

           catch(Throwable t)
                {  
                // log the error
                }       

You can set different log levels to capture events based on user interaction. For example, the EVENT_VIDEO_ACTIONS event in the previous example could be of INFO level when your end user performs a play action. Alternatively, it could be set to WARNING level to capture events in case of failure. You manage the logging level in the logging.properties file. For example, to manage the logging of EVENT_VIDEO_ACTIONS events, configure the logging.properties file as follows:

// Set to  WARNING to log events for the play action. Set to INFO (or a lower level) 
// to log events for the play action plus failure events
oracle.maf.api.analytics.custom.EVENT_VIDEO_ACTIONS.level=WARNING

// Disable logging of EVENT_VIDEO_ACTIONS
oracle.maf.api.analytics.custom.EVENT_VIDEO_ACTIONS.level=OFF

7.4.3 How to Send Context Events to Oracle Mobile Cloud Service

MAF applications can capture context events and send the collected information to MCS or to a repository other than MCS.

A context event defines the context of subsequent events until another context event is logged. An event can be logged from a MAF Framework event or from events that you define in your application.

Table 7-2 lists key names that MCS accepts in the key-value pairs of the JSON object(s) that transmit context events from the MAF application. MCS requires that all properties be of type String. All properties in the following table are optional.

Note:

MCS translates latitude and longitude information to city, state, country, and postal code. Provide latitude and longitude information or locality, region, postalCode and country, but not both.

Table 7-2 Valid Key Names to Send Context Event Information to MCS

Key Name Description
userName The user of the device who was logged in to the secured feature when the context events were logged.
timezone Device's offset from UTC in seconds
model Device's model name
osName Device operating system name
osVersion Device operating system version
latitude Device's GPS latitude
longitude Device's GPS longitude
locality Device's locality
region Device's region
postalCode Device's postal code
country Device's country

To send context event information from your MAF application to MCS, configure the following entry in your logging.properties file:

oracle.maf.api.analytics.McsAnalyticsHandler.contextProviderClassName= oracle.maf.api.analytics.McsContextProvider

With this entry configured in your logging.properties file, MAF sends the context event information listed in Table 7-2 to MCS. You also need to set recordUsername to True in the logging.properties file if you want the MAF application to send the user name to MCS.

If you want to generate context events that contain fields you define, configure the following entry in your logging.properties file:

oracle.maf.api.analytics.McsAnalyticsHandler.contextProviderClassName=oracle.maf.demo.CustomContextProvider

Where oracle.maf.demo.CustomContextProvider is a class that implements oracle.maf.api.analytics.ContextProvider, as shown in Example 7-1. The generated context event contains the timezone, carrier, and manufacturer information.

Example 7-1 Custom Context Provider to Send Context Information

package oracle.maf.demo;

import java.util.Date;
import java.util.TimeZone;
import oracle.maf.api.analytics.ContextProvider;
import oracle.adfmf.json.JSONObject;

public class CustomContextProvider
  implements ContextProvider
  {
  public CustomContextProvider()
  {
    super();
  }
  
  public JSONObject generateContext()
  {
    JSONObject myCustomCtx = new JSONObject();

    //
    // TimeZone - Mobile device's offset from UTC in seconds
    //
    Date date = new Date();
    int offset = TimeZone.getDefault().getOffset(date.getTime()) / 1000;
    
    try
    {
      myCustomCtx.put("timezone", new Integer(offset).toString());
      myCustomCtx.put("carrier", "AT&T");
      myCustomCtx.put("manufacturer", "Apple");
    }
    catch(Exception ex)
    {
      ex.printStackTrace();
    }
  
    return myCustomCtx;
  }
  }

7.4.4 How to Send Analytics to Other Repositories

MAF applications can send analytics to repositories other than MCS.

To achieve this, you write a custom class that extends oracle.maf.api.analytics.McsAnalyticsHandler and overrides processEvent() method, as shown in the following example.

package oracle.maf.demo;

import java.io.IOException;
import oracle.adfmf.framework.exception.AdfException;

import oracle.adfmf.json.JSONArray;
import oracle.adfmf.resource.CDCErrorBundle;
import oracle.adfmf.util.ResourceBundleHelper;
import oracle.maf.api.analytics.McsAnalyticsHandler;
import oracle.maf.api.dc.ws.rest.RestServiceAdapter;
import oracle.maf.api.dc.ws.rest.RestServiceAdapterFactory;

public class CustomHandler extends McsAnalyticsHandler
{
  public CustomHandler()
  {
    super();
  }

  //
  // Establish the connection to a different repository and flush the events.
  //
  protected void processEvents() throws AdfException
  {
    // Extract the events to be flushed
    _events = super.getEvents();

    RestServiceAdapter restAdapter = RestServiceAdapterFactory.newFactory().createRestServiceAdapter();
    restAdapter.clearRequestProperties();

    // Get valid connectionId of the repository.
    restAdapter.setConnectionName(getConnectionId());
    restAdapter.setRequestMethod(RestServiceAdapter.REQUEST_TYPE_POST);
    restAdapter.addRequestProperty("Content-Type", "application/json");
    restAdapter.setRequestURI(_ANOTHER_REPOSITORY_URI);
    restAdapter.setGenerateAnalyticsEvents(false);

    // make REST call to send events
    try
    {
      String responseMessage = restAdapter.send(_events.toString());
    }
    catch (IOException ex)
    {
      throw new AdfException(AdfException.ERROR, ResourceBundleHelper.CDC_ERROR_BUNDLE,
                             CDCErrorBundle.ERR_ANALYTICS_FLUSH_EVENTS, new Object[] { ex });
    }
  }

  private JSONArray _events = new JSONArray();
  private static final String _ANOTHER_REPOSITORY_URI = "_repository_uri";
}

You also need register the custom class in the logging.properties file. For example, to register CustomHandler, configure logging.properties as shown in the following example:

# Configure the analytics logger
oracle.maf.api.analytics.level=ALL
oracle.maf.api.analytics.handlers=oracle.maf.demo.CustomHandler
oracle.maf.api.analytics.custom.level=INFO


# Configure CustomHandler
oracle.maf.demo.CustomHandler.level=INFO
oracle.maf.demo.CustomHandler.connectionId=RepositoryConn
oracle.maf.demo.CustomHandler.batchSize=7
oracle.maf.demo.CustomHandler.offlineWrite=true
oracle.maf.demo.CustomHandler.recordUsername=false
oracle.maf.demo.CustomHandler.contextProviderClassName=oracle.maf.api.analytics.McsContextProvider

7.4.5 How to Add Location Information to Events

By default, client data model does not include latitude and longitude of the current device location in the context information of each event. To add this information, you should set analytics.use.location property in mobile-persistence-config.properties to true.

You need to set the property, as follows:

analytics.use.location=true 

7.4.6 MAF Framework Events that Capture Analytics Information

MAF provides a range of MAF Framework events that capture analytics information.

These events are grouped into two categories (custom and system). Configure your application’s logging.properties file so that the application sends these events to MCS. First, configure your application’s logging.properties file to enable the sending of analytics information by, among other things, specifying the MCS connectionID. You then specify the events that you want to send. Use the information in the following tables for this latter task.

Table 7-3 lists the MAF Framework events (custom category) that you can configure in the logging.properties file to send analytics information to MCS from your application. You can disable each event by setting it to OFF or to a higher logging level than the level that enables logging of the event.

Table 7-3 MAF Framework Events (Custom Category)

Event Name Logging Level Enable or Disable Logging
UpdateAuthenticationStatus FINE

oracle.maf.api.analytics.custom.UpdateAuthenticationStatus.level= FINE

oracle.maf.api.analytics.custom.UpdateAuthenticationStatus.level= OFF

FeatureNavigation INFO

oracle.maf.api.analytics.custom.FeatureNavigation.level=INFO

oracle.maf.api.analytics.custom.FeatureNavigation.level=OFF

Login INFO

oracle.maf.api.analytics.custom.Login.level=INFO

oracle.maf.api.analytics.custom.Login.level=OFF

LoginCallback FINER

oracle.maf.api.analytics.custom.LoginCallback.level=FINER

oracle.maf.api.analytics.custom.LoginCallback.level=OFF

Timer

INFO for Operations: Warning and Expired

FINE for Operation: Adjust

oracle.maf.api.analytics.custom.Timer.level=INFO

oracle.maf.api.analytics.custom.Timer.level=FINE

oracle.maf.api.analytics.custom.Timer.level=OFF

Logout INFO

oracle.maf.api.analytics.custom.Logout.level=INFO

oracle.maf.api.analytics.custom.Logout.level=OFF

LogoutCallback FINER

oracle.maf.api.analytics.custom.LogoutCallback.level=FINER

oracle.maf.api.analytics.custom.LogoutCallback.level=OFF

PageNavigation INFO

oracle.maf.api.analytics.custom.PageNavigation.level=INFO

oracle.maf.api.analytics.custom.PageNavigation.level=OFF

FeatureTransition INFO

oracle.maf.api.analytics.custom.FeatureTransition.level=INFO

oracle.maf.api.analytics.custom.FeatureTransition.level=OFF

ApplicationTransition INFO

oracle.maf.api.analytics.custom.ApplicationTransition.level=INFO

oracle.maf.api.analytics.custom.ApplicationTransition.level=OFF

RESTWebService INFO

oracle.maf.api.analytics.custom.RESTWebService.level=INFO

oracle.maf.api.analytics.custom.RESTWebService.level=OFF

Table 7-4 lists the MAF Framework events (system category) that you can configure in the logging.properties file to send analytics information to MCS from your application

Table 7-4 MAF Framework Events (System Category)

Event Name Logging Level Description
sessionStart INFO MAF reserves the use of this event name to identify the session that it starts when a MAF application activates and starts to log analytics information. Do not define events in your MAF application that use this event name.
sessionEnd INFO MAF reserves the use of this event name to identify the session that it stops when a MAF application deactivates and stops logging analytics information. Do not define events in your MAF application that use this event name.

MAF flushes events to MCS when sessionEnd occurs, irrespective of whether batchSize has been reached or not.

context INFO Sends context event information (for example, the timezone, carrier, and manufacturer of the device). Enable this event as follows:
oracle.maf.api.analytics.McsAnalyticsHandler
    .contextProviderClassName=oracle.maf.api 
    .analytics.McsContextProvider

For more information about the context event information, see How to Send Context Events to Oracle Mobile Cloud Service

7.5 Sending Diagnostic Information to Oracle Mobile Cloud Service

Describes how to insert diagnostic information from MAF applications that access REST web services hosted by Oracle Mobile Cloud service (MCS).

MAF applications that access REST web services use RestServiceAdapter to access these services. If your application accesses REST services hosted by MCS and you want to use MCS Diagnostics to monitor and/or debug your application’s calls to REST services hosted by MCS, create a McsRestServiceAdapter to send the following information to MCS:

  • Mobile diagnostic session ID.

    This attribute maps an application session on a specific device. The application sends this information through the Oracle-Mobile-DIAGNOSTIC-SESSION-ID HTTP request header.

  • Mobile device ID

    Correlates the REST API requests sent to MCS with the physical device that makes the request. The mobile application supplies this information through the Oracle-Mobile-Device-ID HTTP request header.

  • Client request time,

    Indicates the API call time stamp that is captured on the client side immediately before the application submits the request. The mobile application supplies this information using the HTTP request header Oracle-Mobile-CLIENT-REQUEST-TIME attribute.

The following example shows the type of information that a MAF application using this type of adapter inserts into HTTP request headers:
Oracle-Mobile-Diagnostic-Session-ID: 19975
Oracle-Mobile-Device-ID: d09379504b0a3247
Oracle-Mobile-Client-Request-Time: 2016-02-09T09:03:17.777Z

The following example shows how you create the McsRestServiceAdapter:

...
import oracle.maf.api.dc.ws.rest.RestServiceAdapterFactory;
import oracle.maf.api.dc.ws.rest.RestServiceAdapter;

...
RestServiceAdapterFactory factory = RestServiceAdapterFactory.newFactory();
RestServiceAdapter mcsRestServiceAdapter = factory.createMcsRestServiceAdapter();

For more information about creating an adapter, see Creating a Rest Service Adapter to Access Web Services.