A-Team Mobile Persistence Extension for Oracle MAF

Introduction

Oracle has released Oracle Mobile Application Framework (MAF), the new cross-platform development framework for mobile applications that supersedes ADF Mobile. Oracle A-Team has upgraded its Mobile Persistence Extension to work with Oracle MAF. This mobile persistence extension adds significant value to Oracle MAF: it allows easy consumption of RESTful services, and provides a complete persistence layer that allows you to use the mobile application in offline mode. You can read and write data while not connected to the internet, and synchronize any pending data changes later when you are online again. Oracle MAF product development has acknowledged the value of this extension and will incorporate the functionality of this extension in a new release of MAF that will become available later this year.

This article introduces the new MAF release of this persistence extension that you can start using today. It discusses the enhanced REST-JSON wizard in great detail and provides download and getting started instructions at the end of this article.

Main Article

The Case for REST-JSON

With the availablity of Oracle SOA Suite 12c, it has become much easier to convert back-end SOAP web services to REST-JSON services. SOA Suite 12c is a release that focusses on “RESTifying” your back end services, and provides powerful declarative features to publish a RESTful API for mobile consumption. Here are some articles that provide more information about this functionality:

You might wonder why this is so important since Oracle MAF (and its predecessor ADF Mobile) provide strong support for consuming SOAP-based web services. Well, to make a simple and clear statement: if you are planning to build mobile applications, Oracle A-Team strongly recommends to communicate through REST-JSON services with your back-end applications. The reason for this is simplicity and performance. The REST architectural pattern is very easy to work with. The performance overhead with SOAP web services is bigger. XML payloads are more verbose than JSON, which means the size of the data packets sent over the wire is bigger. In the mobile client, XML parsing is a relatively expensive task compared to JSON payloads that can be used directly in JavaScript. So, if you have existing SOAP-based web services you want to consume in a mobile application, we recommend to transform these web services to RESTful services using Oracle Service Bus (OSB) 12c. For new web services that you still need to build, you might consider using JAX-RS which is a standard Java API that makes it fast and easy to disclose your back-end data and business logic through a RESTful API. Note that even if the back-end service is already in REST-JSON format it is still a good idea to virtualize access to it using OSB, decoupling your mobile application from the actual back-end service implementation.

A Sample HR REST API Created with EclipseLink/Toplink RESTful Data Services

If you use the EclipseLink or TopLink JPA implementation that comes with JDeveloper 12c to build your back-end persistence layer, you get a ready-to-use RESTful API for free. Check out this chapter on Exposing JPA Entities Through RESTful Data Services for more information.

The sample back-end application that we will be using in this article was created in 5 minutes by simply running the TopLink/JPA – Entities from Tables wizard using the HR Departments and Employees tables, followed by the EJB – Java Service Facade (JPA/TopLink) wizard. After running the wizards, the project looks like this in JDeveloper:

HrDemoRestProject

We need to add the Toplink-dataservices-web.jar to our ViewController project

ToplinkJar

and then we can deploy the project using the default deployment profile in the ViewController project. We can now test the RESTFul API by entering the following URL in the browser:

http://localhost:7101:/HrDemoRest/persistence/v1.0/query/Department.findAll

This results in the following output:

XMLOutput

As you can see the output is in XML format. The Toplink RESTful data services API supports both XML and JSON, so we need to include the Content-Type header parameter to get the payload in JSON format. To do this, we use the powerful Postman REST client which is a great tool for testing RESTful services

PostmanOutput

Notice the relationships included with the department attributes. This is a typical pattern in RESTful services where the resource is self-describing, it provides subsequent resource paths with the return payload that you might want to traverse to get additional data. For example, we can execute the employeesList1 link to get the employees within department 10:

EmpsInDep

Likewise, we can also execute the employees1 link to get the manager of the department, which will return employee WHALEN as well.

Using the HR RESTful services in an Oracle MAF application

We will now use the A-Team mobile persistence extension to create an Oracle MAF application that uses the aforementioned HR Restful web services and provides CRUD functionality in online mode as well as offline mode. We start with launching the MAF Business Objects from REST Web Service wizard.

WizardLaunch

Note that the Mobile Application Framework category under Business Tier and the associated wizard options only show up once you have installed the persistence extension. After clicking Next on the welcome page we get a page with a security warning:

SecurityWarning

Be aware that by running this wizard you create a mobile application that stores potentially sensitive data on the mobile device. If the device is lost or stolen, this data might fall into hands of people who might abuse it. Although all data is stored encrypted in the on-device SQLite database, you might want to exclude specific data objects, or individual attributes from on-device storage. This can be done by unchecking the Persist checkbox as we will see in subsequent wizard pages.

On the next page we can specify the REST connection:

Step3

We click the plus icon and specify the name of the connection and the URL endpoint.

Step3a

It is convenient to specify as much as possible of the REST resource URL as endpoint. In other words, the part of the URL that is the same for all resources we want to consume should be entered here. This saves you typing on subsequent wizard pages. Note however that clicking the Test Connection button typically does not work here as we specified an incomplete URL.Make sure as well that your endpoint does not end with a slash. If your REST resources are secured you also need to enter the authentication information in the respective fields.

On the next page, we specify the REST resources that return data that we want to use in our application. We want to build an application with departments and its employees. So we specify /query/Department to get department data and /entity/Department/{id}/employeesList1 to get employee data.

Step4

When clicking Next, each resource will be invoked and the payload returned is parsed for candidate data objects.Note that if a collection of data is returned, for example a list of employees, only the first item in the collection is inspected. If this first employee is not a salesman and thus does not have a commission, the commissionPct attribute will not be identified as part of the employee data object. Likewise, if your resource returns a master-detail structure of departments and employees, you need to make sure that the first department contains employees, otherwise employee will not be identified as a (child) data object. Note that on subsequent wizard pages you can manually add data objects and attributes that were missed out initially but that is additional work that you should avoid if possible.

An alternative way to identify candidate data objects and their attributes is to use the Payload column. If you click on that column, a button appears that you can use to launch a dialog where you can enter a sample payload.

Step4a

This option is useful in two situations:

  • If the first item of the live data returned by invoking the REST resource lacks some attributes and/or child data
  • If the REST resource is not available yet because the back-end development team is still working on it

When you specify a sample payload, the REST Resource you entered is ignored and not executed, it will only be used as a default value for the “Find All” resource later on in the wizard.

It is important to understand that the resources we specify on this page are ONLY used to identify candidate data objects that you want to create and use in your application. Later on in the wizard you can specify the exact resources that must be used for the various read and write actions. The values entered here will only be used as default values for the “Find All” resource later on in the wizard. A typical situation where you will use different resources for discovering data objects and the find-all resource, is when your find-all resource returns a small subset of the attributes. For example, you might have a find-all employee resource that only returns employeeId, firstName and lastName, the attributes that you typically will show in a list view. When the user then clicks on an employee in the list and navigates to the detail screen, the additional employee attributes will then be loaded lazily by invoking a so-called “canonical” employee REST resource that returns all attributes for the given employee. This is a common scenario to optimize performance when you are dealing with large data sets where each data object has a lot of attributes. In such a scenario, which is fully supported by this persistence extension, you should use the canonical REST resource in the Data Object Resources wizard page to ensure all employee attributes will be identified.

The Flatten Nested Data Objects checkbox is useful when you have a payload like this:

FlattenPayload

By default, manager will be created as its own data object. If you want to include the two manager attributes directly in the department data object, you should check this checkbox.

Before we actually click the Next button, we need to specify a header parameter to ensure the payload is returned in JSON format rather than XML. We click the Set Headers button, and click Add in the dialog that appears and then we set Content-Type to application/json.

Step4b

Now we are ready to click the Next button. When we do this, we first might get a dialog to enter sample values for the path parameters that we have entered in the resources. In our case, we have specified {id} as the path parameter in the employeesList resource to reference the “current” departmentId, so when we press Next the following dialog will appear:

Step4c

Note that we need to enter a department number from a department that actually contains employees, otherwise the employee data object will not be identified. Now, finally, the next wizard page with candidate data objects will appear:

Step5

We do not need data objects for the relationship link information that is included in the payload, so we uncheck the Select checkbox for these data objects.We modify the class names for the department and employee data objects so we get Java classes generated with nice singular class names. And to support offline mode, we check the Persist checkbox for both data objects.

Step5a

On the next wizard page, the attributes of each selected data object are presented.

Step6

You can set/change the key, required, name, Java type and DB column type of each attribute as desired. For an attribute carrying very sensitive data you can choose to not persist it, which causes the attribute value to be null when running the app in offline mode. You usually need to modify the Java type for the attributes as they typically will show up as java.lang.String. Some attributes may show up as java.math.BigDecimal when the payload value is not enclosed in double quotes. Anyway, you can easily change the Java types using the class picker, and the DB column type will automatically be updated based on the Java type you selected.

It is very important that the key attribute(s) are truly unique. The persistence runtime uses an entity (data object) cache based on the key specified here. If you have multiple data object instances with the same key then they will all show up with the same attribute values: the attribute values of the last instance processed with this key. For a child data object, the reference attribute(s) to the parent might be part of the key. If your payload does not contain such reference attribute(s) you can create them in step 7 of the wizard. You then need to go back to step 6 to mark these “parent-populated” attributes as key attribute.

In the Parent-Child Accessors wizard page, you can set up parent-child relationships between data objects.When you have specified REST resources that return parent and child data in the same payload, and both data objects have been selected, the Accessor drop down list will be pre-populated with these relations. However, in our example the employees working in a department are not included in the payload that returns the department data, instead a separate REST resource must be invoked to get the employee data for a specific department. This is why the Accessor drop down list is empty.

Step7

We can still set-up this parent child relation manually so we can build master-detail screens in the mobile user interface. To create this one-to-many relationship between department and its employees, we click the green plus icon and fill the accessor fields as shown below:

Step7a

Note that the Child Accessor Resource field is prepopulated with the resource we used to identify the employee data object in step 4 of the wizard, which is the correct value in our example. We have set the Child Accessor attribute to employees, which means a getEmployees method will be generated in the department data object, and an employees collection will show under the department node in the data control that we will create later on. After clicking OK the wizard page looks like this:

Step7b

Now we need to define the attribute mapping for this relationship. As you can see, there is no departmentId attribute on the employee data object. However, we need such an attribute so we can restore the one-to-many relationship when running in offline mode. In other words, we need to set up a foreign key relationship between the underlying department and employees tables which will be created by the persistence runtime code when we start the application for the first time. We do this by selecting only the departmentId parent attribute and then clicking on the Add button. This will add a departmentDepartmentId attribute to the employee data object (and a column in the underlying table).

Step7c

Although this attribute is not included in the REST payload when querying the employee details for a department, the persistence runtime code will populate this attribute (and underlying column value) based on the department instance for which the employee details are retrieved.

We also want to display the manager name with a department, so we need to define a one-to-one relationship between department and employee. We click the green plus icon again, and now we define the employee data object as the parent and the department data object as the child.

Step7d

This time we define the parent accessor instead of the child accessor. This is because we want a getManager method in the department data object. We do not need a getManagedDepartments method in the employee data object because the user interface will not show a list of departments managed by an employee. So, indeed, whether or not you need to specify parent and/or child resources depends on the user interface requirements. If you are not (yet) sure about these UI requirements, you can stay on the safe-side and define both accessors. The persistence runtime will only invoke the associated REST resource when the data is actually requested by the user interface.

After clicking OK in the accessor dialog, we need to select the employeeId parent attribute and click the Add button so we can restore this one-to-one relationship in offline mode as well.

Step7e

Now both the department and employee data object have an additional parent-populated attribute. You might have noticed that the name of this attribute is the name of the data object suffixed with the selected parent attribute name.To get clean attribute names it is better to change these attribute names. We can do this by clicking the Back button, select the department data object and rename the employeeEmployeeId attribute to managerId.

Step7f

Likewise, we should rename the departmentDepartmentId attribute in the employee data object to just employeeId. When we click Next we can see the new attribute names in the parent and child attribute lists.

Step7g

We can now click Next  to advance to step 8 of the wizard. On this wizard page we can define the various REST resources and associated HTTP method that should be used for the various read and write actions.

Step8

For the employee service, the resources look slightly different, there is no Find All Resource specified because the employees are retrieved through the child accessor resource we defined before.

Step8a

Note that if your user interface also should provide direct access to all employees, you can still add a Find All Resource here (which would be /query/Employee.findAll). Also note the date/datetime format should match the format of the data attributes in the payload. In the Sort Order field you can define a comma-separated list of attribute names. You can suffix the attribute name with ” desc” to get a descending sort for the attribute. Note that the sort order specified here is the default order in the user interface. The persistence runtime code makes it easy to add UI controls to change the sort order at runtime.

The Quick Search Resource is useful for large data sets. For a large data set you typically do not want to execute a Find All Resource that brings in all instances, as this might cause your device to go out of memory. In such a situation you define a quick search facility in the user interface that will bring in only the instances that match the search criterium. When your data set is not that big, and you use the Find All Resource to get all instances at once, you can execute a quick search filter directly against the on-device SQLite database which is very fast as no web service needs to be invoked.

As discussed above, the Get Canonical Resource is useful in a situation where you have a data object with many attributes, and you only want to bring in all the attributes once a specific data object instance has been selected from a list.

In step 9 of the wizard we can specify parameters and their values for the various REST resources that we defined in step 8. The wizard provides some smart defaulting, but it is good practice to check whether these defaults also make sense in your specific situation.

Step9

Any path parameters, enclosed in curly brackets, that you used in the REST resources specified on the previous page will be pre-displayed. Using the Add button you can add additional query parameters. REST resources that perform a POST, PUT or PATCH typically do not use parameters but instead send the data as a JSON object or JSON array in the payload.In such a case you need to check the Send Serialized Data Object as Payload checkbox.

Step9a

The persistence runtime automatically populates the resource parameter values based on the Value Provider you choose in this wizard:

  • DataObjectAttribute: Populates the parameter with the value of a data object attribute. When using this value provider, you need to set the attribute name using the drop down list in the next column. The data object whose attributes are displayed in the drop down list are determined by the usage of the resource. For example, the above resource returns the employees within a department, so it is assumed you want to select an attribute from the department data object to set the context for the employees list.
  • SerializedDataObject: If the data that must be persisted should be sent through a dedicated parameter, you choose this setting. With this value provider, the other columns should remain empty.
  • LiteralValue: Populates the parameter with a literal value specified in the Literal Value column.
    • ELExpression: Populates the parameter with a value obtained by evaluating an EL Expression. You specify the EL Expression in the Literal Value column. You can use EL Expressions with any scope (applicationScope, pageFlowScope, viewScope, deviceScope, preferenceScope) but it is your own responsibility that the expression is valid in the execution context of the REST service call. Remember that when making transactions in offline mode, the REST service will be invoked later and the EL expression context will then be determined by the task flow and page that triggers the data synchronization.
    • SearchValue: Populates the parameter with the value of the quick search value entered in the user interface. You will typically use this value provider only with the Quick Search Resource that you can define in step 8.

Step10

On this last wizard page you can define the Java packages in which the data object classes and service classes will be generated. If you run the wizard again using the same data object names, for example because payload structures have changed, you can configure whether existing Java classes should be overridden. Note that any custom code that you added to a class will be lost when you regenerate the class. The Overwrite Service Object Classes checkbox is unchecked by default because the service class typically does not need to change when payload structures change, and this is also the class that most likely contains custom code that you added after generation.

When clicking Finish the wizard generator log shows all the files that have been generated:

GeneratorLog

We can inspect the generated files by opening them through the navigator window

GeneratedFiles

The last step we have to take before we can create our user interface is to right-mouse-click on the generated DepartmentService class and choose Create Data Control. In the Create Bean Data Control wizard that appears you can accept all the defaults and click Finish.The Access Mode that you can set in this wizard does not apply to MAF applications. The DepartmentService data control looks like this:

DataControl

You are now ready to build the mobile user interface. You can do this using drag and drop from the DepartmentService data control, or you can use the MAF User Interface Generator wizard that comes with the persistence extension. This wizard is a fast way to generate a default user interface to test all the CRUD operations and data synchronization functionality. The getting started section below includes references to resources that provide more information on creating the user interface, either using drag and drop or using this wizard.

Do not forget to check the Network checkbox on the Device Access tab of maf-application.xml. If you forget this, you will see no data in your mobile application when you run it on a real device.

Getting Started with the A-Team Mobile Persistence Extension

The A-Team MAF Persistence extension is powerful and sophisticated sample code that you can (partially) reuse, extend or copy as desired. You use it at your own risk, there is no official support channel available, however, we are always interested in feedback that you can provide by commenting on this article. The easiest way to get started is to install the extension and watch the videos that come with the extension. Note that the videos are still based on the previous version of the persistence extension that worked with ADF Mobile, but the overall development process is pretty much the same. Here are the files you need for this:

  • adf-mobile-persistence-sample-install.zip (build 12.1.3.0.44, last updated July 2, 2014): This zip file is the JDeveloper extension. Download it, start JDeveloper 12.1.3, go to the Help menu, choose “Check for Updates” and then choose the option “Install from local file”. Select this zip file, and the mobile persistence extension will be installed.
  • Creating a Mobile Application Using SOAP Web Service: This video demonstrates how to generate a fully functional MAF application that reads and writes data using an ADF BC SOAP web service. All data retrieved through web service calls is stored locally on the device in the SQLite database, allowing you to use the application in offline mode. The video also demonstrates the data synchronization functionality that is provided to process transactions made in offline mode.
  • Creating the Business Objects Layer in a Mobile Application Using REST Web Service: This video demonstrates how to generate the business objects layer when using a REST-JSON web service. Generation of the mobile user interface layer is not included in this video as the process is the same as shown in the previous video.

The following sample applications can also be downloaded:

  • HrDemoRest.zip: This is the web application that uses JPA persistence and TopLink data services to expose CRUD functionality as RESTful web services used as the example back-end application in this article. To use this application, unzip the HrDemoRest zip file, open the HrDemoRest.jws in JDeveloper 12.1.3 and modify the connection details in persistence.xml as needed to connect to your local HR schema. Deploy the application by selecting the ViewController project node, right-mouse click and choose “Deploy”. To test whether the deployment is successful, enter http://host:port/HrDemoRest/persistence/v1.0/Model1/query/Department.findAll in your browser.
  • HrDemoSOAP.zip: This is an ADF BC application that exposes ADF BC CRUD functionality as SDO SOAP web services. To use this application, unzip the HrDemoSOAP zip file. Open the HrDemoSoap.jws in JDeveloper and modify the connection details as needed to connect to your local HR schema. Deploy the HRDemoSoap Model application by right-mouse-clicking on HRServiceServiceImpl.java (inside serviceinterface package) and choosing Run. To test the deployed web service, right-mouse-click on HRServiceService.wsdl and choose Test Web Service. The HTTP Analyzer window will appear. Select the findDepartments method in the Operations field, and click the Send Request button. The departments and its employees should now be displayed in response window.
  • HrCrudMobile.zip: This zip contains a finished MAF application that uses the above HrDemoRest and HrDemoSoap applications as remote persistence providers. After unzipping, open HrCrudMobile.jws in JDeveloper to view and deploy the application to a simulator or real device. Make sure you change the IP addresses in connections.xml before you run the sample.

You can also take a look at this presentation which includes some more screenshots to illustrate the development process when using the persistence extension. Note that currently, these slides are still based on the previous version of the persistence extension for ADF Mobile, but apart from some changes in the wizard, the overall development process is still the same.

For a better understanding of the persistence runtime architecture and instructions on using the bean data control, you should check out the article Going Mobile with ADF – Implementing Data Caching and Syncing for Working Offline Part I. This article is based on the previous version of the persistence extension for ADF Mobile, but all information in this article is still valid with the new MAF version. The only difference is that the generated Java classes and persistenceMapping.xml look slightly different. Some of the configuration code that was previously generated in the constructor of the service classes has now moved to the persistenceMapping.xml file. In addition, the generated service classes now contain a number of convenience methods invoking generic superclass methods that should make it more intuitive to use the data control.

Comments

  1. Emilio Petrangeli says:

    Hi All,
    I tried to build an HelloWord application using your framework and using my own RESTfull services, basically just 2 services, one is “User” entity, the second one is “Project” entity. Relationship is: one User can have many Projects.
    I’ve build the DataControl following the example in this page, then I built the UI using MAF User Interface Generator.
    When I run the application in the iOS simulator I get this error:
    [SEVERE - oracle.adfmf.framework - AmxBindingContext - loadDataControlById] Unable to load Data Control DataSynchService due to following error: ERROR [oracle.adfmf.framework.exception.AdfException] – au.com.middiu.test.mobile.model.User.

    Do you have any suggestion about how to fix this?
    Thanks
    Emilio

Add Your Comment