Oracle Blog

A cup of Java with a Cloud of Mobile, served with a French accent

Thursday Dec 18, 2014

The transition from ADF Mobile to the Oracle Mobile Application Framework earlier this year brought us many great new features. Some of them were very visible, such as new Data Visualization components or the new Oracle Alta UI skin. Others are more difficult to find. In this post, I want to introduce you to one of the latter, namely the GeneratedPassword class, which is part of the oracle.adfmf.framework.api package.

The sole function of GeneratedPassword is to generate and manage random passwords. Each of the passwords is identified by a key, which will be used to retrieve it when needed. The passwords are saved to an encrypted credential store, similar to the one used to store user passwords related to login connections. To create a password, simply pass the desired key to the appropriate method along with a seed which will add entropy to the generation algorithm. Subsequent calls to that method using the same key and seed will result in a different password every time.

At this point, maybe you are asking yourself what those random passwords are good for. Personally, I find them very useful to enhance the protection of encrypted SQLite local databases. In the current version of Oracle MAF, it is necessary to provide a password in order to encrypt or decrypt a database. If you hardcode that password, it will be shared by all deployed instances of your application. If one instance is compromised, then all others will be at risk. Using a random password for that use case is a good mitigation measure, since every instance uses a different encryption key. Compromising multiple instance is thus much more time consuming.

Monday Jan 27, 2014

There is a huge difference between the actual performance of an application and the user's perception of that performance. Typically, developers will try to improve the latter by delegating time-consuming tasks to background threads; in other words: asynchronous processing makes it possible to keep the user interface responsive at all times. This is especially important in mobile applications, where network bandwidth and latency can fluctuate wildly in a short time frame. Users are not necessarily aware of changes in network conditions, and thus will readily ascribe any slowdown to the application itself. Consequently, multithreaded programming is an essential part of the mobile developer's tool set.

ADF Mobile applications run on a Java virtual machine. Therefore, they can start threads that will exist in the context of the JVM process. In the current release, the ADF Mobile JVM follows the JavaME CDC specification, which is based on Java 1.4. This means that, unfortunately, the improvements brought by JSR 166(java.util.concurrent) are not available. On the other hand, threads are well integrated in the ADF Mobile framework. They can invoke AdfmfJavaUtilities.invokeDataControlMethod or AdfmfJavaUtilities.setELValue, for example. This makes it possible for you to update the user interface or refresh a bound collection in memory from a thread among other things.

The Apple iOS and Google Android operating systems manage application-related resources themselves. In iOS, when you switch to another application, the current application is suspended. On the other hand, Android's behavior in the same scenario will vary depending on the free memory available on the device. Typically, the processes belonging to an application will continue to run in the background after the switch; when memory is scarce, the operating system may force-kill the process. What happens when you switch away from an ADF Mobile application is thus dependent on the underlying OS. Any threads started by the application process will behave in the same way as the process itself. By default, threads will suspend and resume by themselves on iOS; they will still run in the background on Android.

If you want to implement multithreading in your application, my recommendation is to always manage the state of your threads explicitly and to interrupt them when the application is deactivated or suspended. This will ensure the integrity of your data and will make the application behave the same way independently of the operating system. Interrupting a thread is done by calling theinterrupt() method of the Thread class and by checking the return values of the interrupted() or isInterrupted() methods inside the run() method of the thread or of the runnable. The proper location for the call to interrupt() is a listener class implementing the oracle.adfmf.application.LifeCycleListener interface; such listeners must be registered in adfmf-application.xml. The activate() and deactivate() methods it specifies will be invoked even if the application is killed through the Android task manager. Typically, in addition to interrupt the threads, the application will need to do the following in order to ensure a proper deactivation:

Write any restorable state to an appropriate store

Close database cursors and connections

Defer pending web service requests

Release resources such as files

These tasks can be performed by the threads themselves, by their associatedjava.lang.Runnable instances, or somewhere else. Be careful, though, since activate() and deactivate()will not be called if the application is terminated. It is also possible to implement listeners at the feature level if more granularity is needed. Such listeners implement theoracle.adfmf.feature.LifeCycleListener interface instead. Please note calls to activate() and deactivate() are blocking; you will need to be careful to ensure the application doesn't look unresponsive to the user.

Resource contention is without a doubt one of the greatest challenges any multithreaded application must solve. In ADF Mobile, each local database corresponds to a single file; the SQLite database engine thus implements a complex but reliable locking system. Fortunately, ADF Mobile encapsulates all the complexity. If two threads - each possessing its own JDBC connection to the database - try to write at the same time, no exception will be thrown. One of the threads will own the write lock and will be able to proceed, while the other will wait. In other words: there can be only one database connection in write mode at any given time. All other connections will be in read-only mode until they can acquire the write lock. This will influence the design of your application. For example: if you have to insert a sizable number of records in a background thread, you will perform the operation in smaller batches in order to yield the lock to other threads of higher priority.

Writing a good application is not easy, nor is writing a good performing one. Multithreading can help with the latter, but you must be careful not to waste resources when the application is not in the foreground. After all, performance is not the only component in the user's perception of your application; battery life counts as well...

Among all the questions I got, one was asked very frequently: « How can I transparently retrieve data from the database when a web service call fails? » In other words: how can the local database save my life if the web service doesn't respond? This is a bit different from what I had implemented initially. Thus, I built a new version of the sample application which does exactly that. In the original sample, the code simply detected the presence or absence of a network connection; an available connection meant the web service call was assumed to succeed. Otherwise, an exception was raised and displayed to the user. Thus, the key change to obtain the desired behavior is simply to catch the exception. Then, it is easy to invoke the method that retrieves data from the database instead.

Another nice thing this code snippet demonstrates is how to call JavaScript code from Java business logic. At line 15, I invoke a method which is part of the Apache Cordova library to display a warning message to the user. Cordova is an integral component of ADF Mobile, but your AMX pages must be properly configured in order to use it. I added the proper references to the countriesList.amx page like this:

I placed the script references (lines 3 to 5) inside a verbatim tag, which ensures that they will be rendered as is in the page.

While I was at it, I fixed a few other issues with the sample. In the original version, the database connection was closed inside the stop() method of the LifeCycleListenerImpl class. The stop() method is usuallycalled when the use exits the application; there is no guarantee, however. Thus, the connection wouldn't be closed properly in some corner cases. To fix this, I moved the code to the deactivate() method, which doesn't suffer from the same drawback and will be called each time the user switches to another application. This is much better, as the connection will be properly closed even if the device crashes while the application is inactive.

Friday May 17, 2013

ADF Insider recordings are probable one of the best parts of my job. They are a lot of work, sure. But they are lots of fun to do and join so many members of the ADF Community...

My latest recording is on the ADF Mobile local database. In it, I explore the various aspect of the feature and devote a healthy chunk of time to the management of the database file. The slides contain a few selected code snippets, but I thought it would be better to build a sample application to fully illustrate the concepts. In particular, I wanted to show how it is possible to retrieve data from either a web service or the local database while binding the UI to a POJO Data Control.

My sample application is made is made of two distinct components:

- A simple SOAP web service (SDO view object) built on the top of the HR database schema.

- The ADF Mobile application itself, that demonstrates local database techniques and calls the web service when a network is available. Data is fetched from the local database when their is no connectivity.

I contributed the application to the ADF Enterprise Methodology Group samples repository. It is not listed on the web pages right now, but you can download it from the following location: