In my last post, I told you about my latest ADF Insider Essentials recording on the local database, and pointed you to the companion code sample. I had lots of feedback about both. I am glad to see I have so many viewers and readers!
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.
Here is the relevant method in the sample application.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | private CountryBO[]getCountriesFromWS(){try{ GenericType genericReturnValue =(GenericType)AdfmfJavaUtilities.invokeDataControlMethod("HR_WS",null,"findCountry",new ArrayList(),newArrayList(),new ArrayList()); CountryBO[] returnValue =(CountryBO[])GenericTypeBeanSerializationHelper.fromGenericType(CountryBO[].class, genericReturnValue,"result"); Arrays.sort(returnValue);return returnValue;}catch(AdfInvocationException aie){if(AdfInvocationException.CATEGORY_WEBSERVICE.compareTo(aie.getErrorCategory())==0){ AdfmfContainerUtilities.invokeContainerJavaScriptFunction("oracle.adfinsider.localdb.countries","navigator.notification.alert",new Object[]{"The web service is unavailable. \n\n Data has been retrieved from the local cache.","null","Warning","Ok"});returngetCountriesFromDB();}else{thrownewRuntimeException(aie);}}catch(Exception ex){ Utility.ApplicationLogger.severe(ex.getMessage());thrownewRuntimeException(ex);}} |
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:
1 2 3 4 5 6 7 8 9 10 11 | <amx:panelPageid="pp1"><amx:verbatimid="v1"><scripttype="text/javascript">if (!window.adf) window.adf = {}; adf.wwwPath = "../../../../www/";</script><scripttype="text/javascript"src="../../../../www/js/base.js"></script><scripttype="text/javascript"src="../../../../www/js/cordova-2.2.0.js"></script></amx:verbatim><amx:facetname="header"><amx:outputTextvalue="#{viewcontrollerBundle.COUNTRIES}"id="ot1"/></amx:facet> ...</amx:panelPage> |
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.