Skip to content
This repository was archived by the owner on Mar 28, 2019. It is now read-only.

Working with Storage Objects in Java Code

Steven Davelaar edited this page Apr 1, 2016 · 2 revisions

Programmatic access to the StorageObjectService class is no different from Getting Programmatic Access to Entity CRUD Services. You can use the default constructor, or the constructor which allows you to override the persistence mapping values of remoteReadInBackGround and remoteWriteInBackground which is typically easier in Java code so you directly work with the results of the REST calls. Here is a code snippet to get all storage objects in a collection

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

###Associating Storage Objects with Custom Entities When building mobile apps, there is often a requirement to enrich existing data from a backend system of record with files that can easily be captured using the mobile device camera. In this section we show the Java code that is required to add an employee picture to an employee list view and employee form page where the employee data is coming from a custom MCS API and the employee pictures are stored in the MCS storage "HR" collection. And we will show how you can add a picture for a new employee using the device camera. To be able to associate the picture with the correct employee, we will use the following naming convention for employee pictures:

EmpImg[EmployeeID suffix] , for example: EmpImg100

We start with adding an image property of type StorageObject to the Employee.java class, 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 we call findOrCreateStorageObject method passing 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 Runnable using a Java 8 lambda expression. This allows us to refresh the UI once the employee picture is downloaded. Finally, we call the getDownloadIfNeededInBackground method which calls MCS to download the file and update the storage object metadata if needed. Note that if the file has been downloaded before, AMPA will first make a so-called HEAD call to check wether the ETag of the storage object in MCS has the same value as the local ETag value stored in the SQLite database. If it has changed, the file will be downloaded.

The download location 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 setDirectoryPath on the StorageObject.

With the picture StorageObject added to the Employee class, we can now drag and drop the picture filePath property to add an image to our list of employees within a department:

When we navigate to the department for the first time, you will see a slight delay before the employee pictures show up: for each employee in the department a parallel REST call is made in the background to fetch the picture from MCS. The next time when you run the app the images will appear immediately.

By default, an error message will be shown when an employee doesn't have a picture yet in MCS, because the REST call will result in a 404 Not Found response:

This can easily be avoided by setting the showWebServiceInvocationErrors property in persistence-mapping.xml to false for the StorageObject descriptor:

<classMappingDescriptor className="oracle.ateam.sample.mobile.mcs.storage.StorageObject" persisted="true">
  <crudServiceClass className="oracle.ateam.sample.mobile.mcs.storage.StorageObjectService"
                  autoIncrementPrimaryKey="true"
                  localPersistenceManager="oracle.ateam.sample.mobile.v2.persistence.manager.DBPersistenceManager"
                  remotePersistenceManager="oracle.ateam.sample.mobile.v2.persistence.manager.MCSStoragePersistenceManager"
                  remoteReadInBackground="true" remoteWriteInBackground="true" 
                  showWebServiceInvocationErrors="false"
                  autoQuery="false" enableOfflineTransactions="true"/>

To upload a new employee picture, we add a commandLink to the employee form page. This link should call a managed bean method that use standard Oracle MAF code to take a picture using the camera, or select a picture from the camera roll:

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 the picture is taken, we obtain the current Employee instance, and call method addPicture on this employee:

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, we first get 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 will save the storage object metadata in the SQLite database, stream the picture to the file system and it will call MCS to add the file to the HR collection. After the file is saved, we can remove the temporary file created by the device camera to free up disk space.

Note that 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, you can add a removePicture method to the Employee Java class and then 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.

Clone this wiki locally