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:
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.
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" }
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:
What Happens When You Create the StorageObject Bean Data Control
How to Retrieve All Files from an Oracle Mobile Cloud Service Storage Collection
How to Filter the List of Storage Objects from an MCS Storage Collection
How to Retrieve a Single File from an Oracle Mobile Cloud Service Storage Collection
How to Associate Storage Objects with Data in your MAF Application
Describes how to create and use the StorageObjectService
data control that you can use to retrieve and manage objects in an MCS storage collection.
StorageObjectService
bean data control:JDeveloper generates the StorageObjectService
data control in the Data Controls panel.
Figure 7-2 Generated StorageObjectService Data Control
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>
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
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>
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.
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
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 aHEAD
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
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
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 usingDeviceManager.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.
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:
How to Configure the Transfer of Analytics to Oracle Mobile Cloud Service
How to Programmatically Send Analytics to Oracle Mobile Cloud Service
MAF Framework Events that Capture Analytics Information (Describes the MAF Framework events that MAF provides.)
For information about the classes in the oracle.maf.api.analytics
package that you can extend and customize, see Java API Reference for Oracle Mobile Application Framework.
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 fororacle-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 |
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
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; } }
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
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
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 |
|
FeatureNavigation |
INFO |
|
Login |
INFO |
|
LoginCallback |
FINER |
|
Timer |
INFO for Operations: Warning and Expired FINE for Operation: Adjust |
|
Logout |
INFO |
|
LogoutCallback |
FINER |
|
PageNavigation |
INFO |
|
FeatureTransition |
INFO |
|
ApplicationTransition |
INFO |
|
RESTWebService |
INFO |
|
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 |
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 |
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.
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.