Introduction

OraclePermissionGeneratorAndroid is an example of a simple Android-based business application, implementing the MVP (Model View Presenter) design pattern, and demonstrating how an Android application can interact with a .NET WCF webservice via SOAP or REST protocols.

Background and Motivation

Mobile devices already represent a major slice of the personal computing landscape, as users move from PCs to phones and tablets. A similar transition is occurring also in enterprise/business computing, where many companies have already adopted BYOD (bring your own device) policies, and are deploying mobile-capable software for staff. It's natural that this transition will continue and accelerate in the future. As an engineer of enterprise software, I felt it was essential to get some exposure to development on a mobile device platform. Android has the largest installation base of any operating system, and is used by over 20,000 unique types of devices (current as of 2015). Given that it's easily accessible to developers (freely available SDK and IDEs) I decided to develop a simple Android application to get some experience on a mobile platform.

In 2013 I released a .NET / Windows Forms application called Oracle Permission Generator. This was a simple desktop application which allowed users create and manage user permission scripts for Oracle databases. My goal in writing a mobile application was to create a version of Oracle Permission Generator for the Android platform. The original version of Oracle Permission Generator was built using the MVP (Model View Presenter) design pattern which meant that all the business logic (the Model part of the pattern) was clearly separated from the user interface code. I wanted to reuse the original .NET business logic (MVP Model) code unaltered, exposing it as a webservice via WCF, and reimplement the View and Presenter components as an Android application.

Why MVP?

In the past I had written several desktop applications using the MVP pattern. Although it can take time to understand, and can seem overly complex for simple applications, I had grown to really like the MVP pattern because of the following benefits...

  • It lends itself to producing code that is easily unit testable, and hence you can achieve a high degree of unit test coverage across your project
  • It allows new features and changes to be incorporated into the application quickly and easily
  • It allows you to make major underlying refactoring to the code (major structural changes etc...) relatively easily (in part because of the high degree of testability described above)
  • Because code concerns are kept clearly separated (e.g. user interface from business logic), it allows your application to grow quite large if required, without becoming prohibitively complex
  • It lends itself to improved code reusability... as a basic example, common business logic functionality can be used by multiple screens in the application

Given this experience, and as my Android project would be a GUI application, it was a natural choice to use the MVP pattern.

Installing and Opening the Projects

See the instructions for installing and opening the projects.

Android Basics

This section gives a brief overview of the main Android components and classes that are required to build a basic application.

Activity

An Activity in Android is an abstract component representing 'a single, focused thing that the user can do'... it can basically be through of as a single screen in an Android application, somewhat analogous to a Form in Windows Forms, or a Window in Java AWT/Swing. The AddRoleToUserMapActivity class in OraclePermissionGeneratorAndroid is a good example of a simple Activity.

Service

A Service is a component in an application which does not have a user interface, and is used to implement long-running background tasks. There are two sub-types of services...

  • Started services - these are usually used to perform one-off long-running tasks where the service stops when the task is complete. An example would be uploading a large file to a remote location.
  • Bound services - Bound services are designed to provide reusable background processing. Client components can bind to the service, and the service keeps running as long as the clients continue to run.

In OraclePermissionGeneratorAndroid (hereafter referred to as 'OPG Android'), the Android-side Model layer (which proxies business logic / data layer method calls to the C# webservice) is implemented using a bound service (in class datainterfacelayer.DataInterfaceService).

Views / ViewGroups

View is a superclass of many of the UI components which can be placed on Activities (similar to 'Controls' or 'Widgets' in other GUI frameworks). Examples include TextView, ListView, EditText, Spinner, and Button. As an example, the AddRoleToUserMapActivity mentioned above contains 3 TextViews, 2 EditTexts, and a Button. ViewGroups are used to define collections and arrangements of Views.

Intent

An Intent is a somewhat abstractly-named class which has many uses in Android applications. In OPG Android they are used to...

  • Start (open) Activities from other Activities
  • Start or bind to services
  • Send data to other applications (specifically when generating permission scripts in OPG Android)

XML

XML is used fairly extensively in Android projects...

  • To define the layout of Views and ViewGroups inside Activities (see the 'res\layout\' folder in the OraclePermissionGenerator project)
  • To define menu layouts (see the 'res\menu\' folder)
  • To store application common constant values (strings defined in Activities, dimensions of View components etc... in the 'res\values\' folder

An Android project also includes a master 'AndroidManifest.xml' file which contains many fundamental application settings.

R class

An Android project contains an R class which is generated by the compiler. This class provides a 'bridge' between pure Java classes (e.g. Activities), and classes that are defined in the XML files (e.g. Views defined in the 'res\layout\' folder). The R class is located in the 'gen\' folder of an Android project.

Basics of MVP

There is a lot of information available online about the MVP pattern, but the basic principle of the pattern is to separate the business logic and user interface concerns (as is also the case with other 'MV' patterns like MVC and MVVM). Business logic and domain model concerns are part of the Model layer of the pattern. User interface concerns form the View layer. The presenter is basically the 'glue' between the View and the Model and controls all interactions and data passing between them. Clear interfaces are defined for each of the 3 layers.

By virtue of enforcing these separations of concern, and having interfaces defined for each of the layers, it's easy to write very unit testable code using MVP. The idea is to make the code in the View(s) as simple as possible, so that unit tests are not required around this layer (as user interface components are typically difficult to unit test). Any control flow code in the application should be incorporated into the presenter, and business logic and domain model code into the model. As the presenter and model don't contain any UI components, it's straightforward to write unit tests around them.

The sequence diagram below shows the interactions (i.e. method calls) between the 3 layers when the user clicks the 'Add Mapping' button in the 'Add Role to User Mapping' screen (class AddRoleToUserMapActivity) in OPG Android. This sequence of interactions between the layers is typical for an MVP application. The key point is that there is no direct interaction between the Views and the Model... all such interaction is handled by the Presenter...

MVP Layer Interactions Sequence Diagram

In OPG Android, each of the 3 MVP layers have the following responsibility...

View

In line with the MVP paradigms, the View layer is kept as simple as possible. Views are implemented as Android Acitvities. Methods to manipulate the UI (e.g. populate a field) usually just pass data directly to a subclass of the Android View*. Interactions with the UI (e.g. intercepting button clicks) are generally just passed directly to methods on the Presenter.

Presenter

As well as managing interactions between with View and Model layers, the Presenter in OPG Android handles the following...

  • Managing worker threads for long running operations using the AsyncTask class.
  • Handling and logging of exceptions.

Model

  • Proxying of business logic and domain model interactions and requests to the .NET version of the Model layer.
  • Interaction with the Android OS (e.g. to get the location of the device).
  • Saving and retrieving of application settings.

* Note that the term 'View' here is used in two different contexts... the MVP View layer (an abstract grouping of code which provides the user interface), and the Android View class (a superclass in the Android API which is a building block for user interface components). The term will be used in both contexts throughout this document, so it's important to understand the distinction.

The diagram below gives an overview of the main classes in OPG Android, and where they fit into the MVP pattern...

Overall Application Architecture

MVP vs Android Paradigms

There are some fundamental differences between the MVP pattern and some of the practices that are advocated and demonstrated in the Android API and sample projects on the web. These are summarised below...

Practice MVP Android API / Samples
Data passing between activities All data is passed through the Presenter Data is passed directly from one Activity to another using Intents
Access the to domain model / business logic Channelled through the Presenter Activities access domain model / business logic directly (e.g via an Android Service)
Exception handing and control flow code Implemented in the Presenter Implemented in Activites (code can potentially be reused by putting common code in base Actvities)

The Android Intent class is used to start Activities from other Activities. The Intent class includes a method putExtra() which allows name/value data pairs to be passed to the Activity being started. The value of the name/value pairs can be primitive data types, arrays of primitive data types, or classes implementing the android.os.Parcelable or java.io.Serializable interfaces (essentially objects that can be serialized). Using the Intent.putExtra() method to pass data between activities is demonstrated by many Android sample projects on the web. However this is contrary to the MVP pattern where Views should not communicate directly with other Views, and all data should be passed via the Presenter.

Many Android sample projects access their domain model and business logic by having Activities connect directly to Android Services. This also breaks the MVP paradigm, where all access to the domain model and business logic should be channelled through the Presenter. Similarly many Android sample projects implement exception handling and control flow via code in Activities (examples of control flow are often Activities starting other Activities as described above). In MVP control flow is handled by the Presenter. Exception handling would usually be managed by either the Presenter or the Model layers in MVP (in OPG Android I chose to implement it in the Presenter).

The diagram below gives an overview of component interactions as demonstrated in many Android sample projects...

Android Architecture

...whereas this depicts the same interactions using the MVP pattern applied to the Android framework...

MVP Architecture

The Challenges in Implementing MVP in Android

Establishing References Between the Views and Presenter

In the MVP pattern, the relationship between a View and Presenter is bi-directional. This allows interactions to occur from the Presenter to the View (e.g. populating fields in the view) and also from the View to the Presenter (e.g. handing a user action like a button click). To implement this bi-directional relationship a reference to the Presenter class is required from the View and vice-versa. In languages and frameworks where the creation of UI classes is explicitly controlled by the programmer, implementing such a relationship is straightforward. The Java sample below shows how a UI class could be created and have a reference to the Presenter class set on it (assuming MainView is a subclass of javax.swing.JDialog)...

MainView mainView = new MainView(); mainView.SetPresenter(presenter);

In Android, the creation of new Activities is instead effectively sent to the Android operating system as an Asynchronous request (via an Intent). As a result there is no straightforward way for the code creating the Intent to get a reference to the new Activity that is created as a result of the request. The Intent class has the putExtra() method (discussed above) which can send data to new activities, but this is limited to sending primitive data types and serializable classes (essentially it can pass copies of data, but not references to classes). Hence there isn't a straighfoward way to create a reference from the View to the Presenter equivalent to the above Java code. I considered three possible ways to work around this...

  1. Implement the Presenter as an Android Service
  2. Implement the Presenter as a singleton
  3. Use a singleton class to hold a reference to the Presenter

I steered away from implementing the Presenter as an Android Service, mainly because Services can be subject to forced shutdown if the Android OS is low on memory. As the Presenter is the central component in the application, having to recreate it during runtime would mean re-establishing a lot of reference to Views (Activities) which would potentially introduce a lot of additional complexity. I generally try to avoid using singletons if possible, so I was not keen on implementing the Presenter as a singleton (also doing this could have created extra complexity in unit testing... e.g. how could I call a special constructor on the Presenter to pass in the mocks used for the unit testing case, if it was implicitly created as a singleton?). I settled on the third option to use a separate singleton class to hold a reference to the presenter. This meant the Presenter class could still be instantiated explicitly, and instantiated with a special constructor for the unit test case, but also a reference to the Presenter could be obtained from the singleton class. I created a simple singleton called PresenterGlobalStorer to hold the presenter...

public final class PresenterGlobalStorer { private static IPresenter presenter; /** * @param value The application's presenter component. */ public static void setPresenter(IPresenter value) { presenter = value; } /** * @return The application's presenter component. */ public static IPresenter getPresenter() { return presenter; } private PresenterGlobalStorer() { } }

Then to get a reference to the Presenter from an Activity, I access the PresenterGlobalStorer from the Activities' onCreate() method. The below example is from the AddRoleToUserMapActivity class (note the 'presenter' member is defined in the BaseActivity superclass)...

@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.add_role_to_user_map_activity); presenter = PresenterGlobalStorer.getPresenter(); presenter.setAddRoleToUserMapView(this); }

The onCreate() method is called by the Android OS as part of the process of creating a new Activity from an Intent. Hence following the above pattern results in the correct bi-directional relationship with the Presenter being established each time a new Activity is created.

Working with the Android Activity LifeCycle

Working with the Android Activity lifecycle provided another challenge. A key point is that the Android OS has the ability to kill an Activity to recoup system memory. As references are maintained between Activities (MVP Views) and the Presenter, my concern was that the reference from the Presenter to an Activity could delay or prevent garbage collection of killed Activities, or potentially create 'orphaned' Activities which were still accessed by the Presenter, but which the Android OS considered to be closed or killed. To attempt to avoid these situations, I overrode the onDestroy() method in each Activity (this method is called by Android before killing any Activity as part of the Activity lifecycle), and reset the reference from the Presenter. The below code is a sample of the AddRoleToUserMapActivity class (again the 'presenter' member is defined in the BaseActivity superclass)...

@Override public void onDestroy() { presenter.setAddRoleToUserMapView(null); super.onDestroy(); }

I hoped that would avoid any issues with preventing garbage collection or creating 'orphaned' Activties (but have not as yet done extensive testing of the application's interaction with the Activity lifecycle).

Supporting Multiple Users

The original Windows Forms version of Oracle Permission Generator was a single user application. I wanted to reuse the original business logic (Model) code, but also support multiple users in the Android version. Most of the key business logic and domain model functionality was encapsulated in a class called OraclePermissionGeneratorDataInterfaceLayer (a class composing several more granular areas of functionality and exposing them following the facade pattern). The simple approach I took to implementing multi-user functionality, was to create a .NET Dictionary where the dictionary value was an OraclePermissionGeneratorDataInterfaceLayer class, and the key was a string representing a unique id for a user. This allowed each unique user to access their own copy of the data model. This Dictionary is created in the Program class, and injected into the SoapWebServiceApi and RestWebServiceApi classes which expose the Model layer via SOAP and REST respectively...

using (FileTrackingDataLogger trackingDataLogger = new FileTrackingDataLogger(@"C:\Temp\")) { Dictionary<string, OraclePermissionGeneratorDataInterfaceLayer> userDataRespository = new Dictionary<string, OraclePermissionGeneratorDataInterfaceLayer>(); SoapWebServiceApi soapApiInstance = new SoapWebServiceApi(userDataRespository, trackingDataLogger); RestWebServiceApi restApiInstance = new RestWebServiceApi(userDataRespository, trackingDataLogger);

Generally, the SoapWebServiceApi and RestWebServiceApi classes simply proxy method calls to the relevant OraclePermissionGeneratorDataInterfaceLayer object in the Dictionary (this is mostly performed by base class WebServiceApiBase). As an example, the code to add a new mapping between a role and user appears below (member 'userDataRepository' is the Dictionary class)...

/// <summary> /// Adds a role to user mapping to the master lookup list. /// </summary> /// <param name="role">The name of the role.</param> /// <param name="user">The name of the user.</param> /// <param name="authenticationContext">The authentication context of the web service consumer or user.</param> /// <param name="trackingData">Tracking information of the web service consumer or user.</param> public virtual void AddRoleToUserMap(String role, String user, String authenticationContext, String trackingData) { // Deserialize parameters AuthenticationContext deserializedAuthenticationContext = jsonSerializer.DeserializeAuthenticationContext(authenticationContext); // Call data layer method ValidateUser(deserializedAuthenticationContext.UserIdentifier); userDataRepository[deserializedAuthenticationContext.UserIdentifier].AddRoleToUserMap(role, user); LogTrackingData(deserializedAuthenticationContext.UserIdentifier, "AddRoleToUserMap(role, user)", trackingData); }

For the purposes of a simple prototype, this Dictionary solution for multiple users is sufficient. However for real production code it couldn't be used for the following reasons...

  • The Dictionary object is not thread safe, so there is no guarantee of consistent operation if multiple users access the Model layer concurrently.
  • No method of persisting the data has been implemented, so if the .NET code stops, or server shuts down, all the user's data is lost.
  • There is no ability to load balance amongst multiple servers, which would be required for a real, large-scale mobile application.

Synchronous vs Asynchronous Operation

Most consumer applications written for Android and other mobile platforms like IOS follow an asynchronous design paradigm. Responses to user interaction are processed in the background, and the user interface remains fully operational at this time, potentially allowing for additional background operations to be processed in parallel. This type of paradigm does introduce some extra complexity though. Say for example the connection to the server is lost whilst a background operation is being processed... the UI may have already been updated anticpating the operation would be successful (e.g. adding a new item to a list), and the user may have moved on to another operation (like updating the new item). In the case the background operation fails, the UI and server side get out of sync, and some mechanism is required to resolve this. Applications often use client-side caching of operations to handle these types of cases (i.e. the operation can be cached in the device and synchronised with the server when the connection becomes available again). However, what happens if the loss of connection cannot be resolved in time... e.g. if there are dependencies on the new item being sent to the server within a specified timeframe? You could elect to notify the user of the failure to synchronize after a set timeout/retry period, but what if the user was no longer using the device when that notification came? Ofcourse mechanisms can be built to work around and solve these problems, but they make the process of interacting with the server inherently more complex.

My goal with this project was to build an Android application for a business context. Often for these types of applications, having the possibility of a failure notification being missed is not an option. As a basic example, say in a financial markets trading application the user inputted a trade, but due to a connection issue the trade didn't go through, and the user wasn't immediately notified... such a situation could result in financial loss and hence wouldn't be acceptable to the user. So for that reason I chose to adopt a more synchronous paradigm for OPG Android. When a background process with the server is started, the user interface is blocked (preventing further user interaction) until one of the following situations eventuates...

  1. The server process succeeds, the user interface is updated accordingly and unblocked.
  2. An error occurs in the server process, and the user is immediately notified (potentially allowing them to subsequently retry the operation)

This type of paradigm has some drawbacks, most significantly...

  • Any operation interacting with the server blocks the user interface with a 'please wait' dialog, even comparatively small operations like ticking a checkbox
  • The interface with the server becomes quite 'chatty' (i.e. multiple small interactions with the server for a single user operation) which is unfavourable on mobile devices

... however it does mean that the outcome of any server interaction can be more deterministic, and more reliable, which is better suited to enterprise/business applications.

For consumer applications an asynchronous approach is usually preferable. Some patterns to implement asynchronous service interaction are discussed in this presentation from Google I/O.

Running Background Processes using the AsyncTask class

Much like other application frameworks which support a GUI, Android requires that any interaction with the user interface be done from the main thread of the application. To allow long running processes (like accessing webservices over the internet) to execute on a worker thread and then update the user interface on the main thread, the Android API provides a class called AsyncTask. The AsyncTask class provides similar interface and functionality to the BackgroundWorker class in the .NET framework. In other frameworks it's possible to execute long running processes on the application's main thread (at the expense of potentially making the user interface unresponsive), however in Android long running tasks must be executed on a worker thread. The Android OS constantly monitors applications for responsiveness of the user interface, and if it detects that an application is unresponsive, it will show an 'Application Not Responding' dialog and prompt the user to close the application. The OS monitors applications frequently, and these 'Application Not Responding' dialogs show very quickly if long running tasks are executed on the main thread.

In OPG Android, all long running processes followed the same pattern of operation...

  1. Access the Model layer via C# webservice to retrieve or update data
  2. Adjust the View layer accordingly (i.e. open an Activity, populate an Activty with data, close an Activity, etc...)

Additionally, any access to the C# webservice was dependent on the availability of a network connection, so I wanted to be able to handle the case of the device being disconnected, and give the user the option to retry when the connection was available. Finally, in the case of an unexpected exception occurring, I wanted to display a 'friendly' message to the user, and store details of the exception on the device. I wrote a subclass of AsyncTask called ExceptionHandlingAsyncTask which manages worker threads and provides the following additional functionality...

  • Displays a 'Please wait' dialog box on top of the current Activity whilst the worker thread is executing. This avoids the additional complexities involved with asynchronous operation discussed above, but also keeps the main thread responsive, and prevents the Android OS showing 'Application Not Responding' messages.
  • Shows a 'Connection error' message if the background process fails due to a network issue (i.e. Java IOException), from which the user can subsequently retry the operation.
  • Displays a 'Critical error' dialog to the user if an unexpected exception occurs, and attempts to log the exception to the device's storage before closing the application.

As the ExceptionHandlingAsyncTask class is only used by the Presenter, it's implemented as an inner class inside the Presenter. The class facilitates most of OPG Android's basic functionality. The below code gives an example of using the ExceptionHandlingAsyncTask to add a role to user mapping to the application...

public void AddRoleToUserMap(String role, String user) { ExceptionHandlingAsyncTask<String, Void, ValidationResultContainer<String[]>> addRoleToUserMapTask = new ExceptionHandlingAsyncTask<String, Void, ValidationResultContainer<String[]>>(addRoleToUserMapView) { @Override protected ValidationResultContainer<String[]> doInBackground(String... parameters) { ValidationResult validationResult = null; try { CheckDataInterfaceServiceConnection(); validationResult = dataInterface.RoleToUserMapValidate(parameters[0], parameters[1]); if (validationResult.getIsValid() == false) { return new ValidationResultContainer<String[]>(validationResult, new String[] { parameters[0], parameters[1] }); } dataInterface.AddRoleToUserMap(parameters[0], parameters[1]); } catch (Exception e) { doInBackgroundException = e; } return new ValidationResultContainer<String[]>(validationResult, new String[] { parameters[0], parameters[1] }); } @Override protected void onPostExecute(ValidationResultContainer<String[]> validationResultContainer) { if (HandleBackgroundException() == false) { if (validationResultContainer.ValidationResult.getIsValid() == true) { // Add the mapping to the role to user map view RoleToUserMap newRoleToUserMap = new RoleToUserMap(validationResultContainer.PostExecuteParameter[0], validationResultContainer.PostExecuteParameter[1]); roleToUserMapView.AddRoleToUserMap(newRoleToUserMap); notificationDialogDisplayView.Close(); } else { // Show an error dialog with the results of the validation // Create a class to define the action to take when the user clicks the 'OK' button on the alert dialog OnClickListener alertDialogConfirmationAction = new OnClickListener() { @Override public void onClick(DialogInterface arg0, int arg1) { // Close the alert dialog notificationDialogDisplayView.CloseOkDialog(); } }; notificationDialogDisplayView.ShowOkDialog("Error", validationResultContainer.ValidationResult.getValidationError(), alertDialogConfirmationAction); } } super.onPostExecute(validationResultContainer); } }; addRoleToUserMapTask.execute(role, user); }

Connection to the C# Webservice (MVP Model layer)

One of my goals with OPG Android was demonstrate multiple methods of communicating with a webservice, namely REST/JSON and SOAP/XML as the means of transport protocol and data serialization respectively. Since REST/JSON was already implemented in many other Android applications, I started with the SOAP/XML webservice.

Differences between Oracle Java and Android Java

Given there were already comprehensive Java libraries available for hosting and consuming web services, at the very start of the project (before having setup the Android libraries, IDE, etc...) I started building Java code to consume a SOAP .NET webservice using Apache CXF framework. I spent a fair amount of time on this, before porting code over to Android, and realizing it wouldn't compile! An important distinction to understand is that Oracle Java and Android Java are not the same... aside from running on different underlying Java virtual machines, some packages (specifically parts of javax which are required by CXF) are not included as part of Android Java. Support for consuming SOAP webservices is available in Android using the ksoap2 library.

Using ksoap2 to Consume SOAP Webservices

Ksoap2 is an open source package which allows you to consume SOAP webservices in Android. To connect to webservices, and send SOAP messages using ksoap2 is relatively straightforward. However to serialize and deserialze to/from Java objects and XML using ksoap2 is somewhat tedious, requiring that you manually iterate through an XML document and build your Java objects by extracting data from the document. It requires a lot more bespoke code as compared to CXF (where a pre-compilation step builds the serialization/deserialization code for you), and given there were quite a number of container objects in OPG Android, I was reluctant to spend too much time on writing a lot of custom serialization code. Comparitively, the org.json package which is included with Android for serialization/deserialization to and from JSON objects allows this type of serialization code to be built more simply. So for that reason I decided to use SOAP as the transport protocol, but JSON (rather than the usual XML) for object serialization.

Implementing the Application Model Layer as an Android Service

The Model layer of the application (containing the business logic and domain model) was implemented as an Android service. As most of the Model layer function sends webservice requests to the .NET version of the Model layer, it suited the design intention of the Android service... i.e. to perform long running background operations without a user interface. The Model layer is implemented in class datainterfacelayer.DataInterfaceService (note, as this class effectively aggregates all of the business logic, domain model, and hence MVP Model functionality of the application, I will refer to it as the 'data layer' of the application going forward). In order to function correctly as an Android service, DataInterfaceService subclasses the Android Service class and includes the following...

  1. Declaration of an inner DataInterfaceServiceBinder class extending the Android Binder class...

    /** * Binder class that is returned by the DataInterfaceService class * @author Alastair Wyse */ public class DataInterfaceServiceBinder extends Binder { /** * Returns the outer DataInterfaceService class. * @return The DataInterfaceService. */ public DataInterfaceService getService() { return DataInterfaceService.this; } }

  2. Inclusion of a private final DataInterfaceServiceBinder member...

    private final DataInterfaceServiceBinder binder = new DataInterfaceServiceBinder();

  3. Overriding the Service.onBind() method to return the Binder member when called...

    @Override public IBinder onBind(Intent arg0) { return binder; }

For the Presenter to connect to the DataInterfaceService, it must do so through a subclass of the Android ServiceConnection class. This subclass should override the onServiceConnected() and onServiceDisconnected() methods, and in case of OPG Android I wanted to set a reference from the Presenter to the DataInterfaceService when the DataInterfaceService was started/connected. As such, I implemented the class DataInterfaceServiceConnection as an inner class of the Presenter...

/** * Implementation of the ServiceConnection class to facilitate connecting to the android service which provides the data interface for the application. * According to Android documentation (http://developer.android.com/reference/android/content/ServiceConnection.html), the callback methods defined in this interface are called from the main thread of the process, hence locks are not placed around access to the 'dataInterface' member. */ private class DataInterfaceServiceConnection implements ServiceConnection { @Override public void onServiceConnected(ComponentName name, IBinder service) { // Cast the binder to a DataInterfaceServiceBinder, and get the DataInterfaceService from it. DataInterfaceServiceBinder binder = (DataInterfaceServiceBinder)service; DataInterfaceService dataInterface = binder.getService(); dataInterface.Initialize(); Presenter.this.dataInterface = dataInterface; dataInterfaceServiceConnected = true; } @Override public void onServiceDisconnected(ComponentName name) { dataInterfaceServiceConnected = false; } }

The actual creation of the connection/binding from the Presenter to the DataInterfaceService occurs in the Presenter's private method CheckDataInterfaceServiceConnection(), which is called as the first step in any Presenter method which needs to connect to the data layer. The DataInterfaceService is actually started by first creating an Intent, and then passing that Intent and a reference to the DataInterfaceServiceConnection class to the bindService() method on the main Activity of the application (ObjectListActivity). Having a dependency on an Activity (actually an Android Context which is a superclass of Activity) is a little unfortunate, as it doesn't work well with the MVP pattern. As per the pattern, the Presenter does not hold view references as concrete Activity classes, but rather interfaces. This means that to access the Context classes' methods, the view reference must be cast to a Context. This can subsequently cause problems with unit tests where the view references are mocks rather than concrete Activities/Contexts. Furthermore, from the MVP theory perspective, the pattern is supposed to enforce separation between the View and Model components. By creating a coupling between them in the bindService() call, it breaks the MVP paradigm and has the potential to also impact other benefits that the pattern offers (like unit testing as mentioned). Unfortunately there's no avoiding this in the Android API, as a Context object is required to be able to start or bind to a Service. The full code of the CheckDataInterfaceServiceConnection() method appears below...

/** * Checks that the data interface service is connected, and if not connects. * <b>Note</b> - This method attempts to cast private member objectListView to a Context, and hence will likely break if used in unit tests where the object list view is mocked. */ private void CheckDataInterfaceServiceConnection() throws Exception, InterruptedException { if (dataInterfaceServiceConnected == false) { Context context = (Context)objectListView; // Create an intent to bind to the data interface service Intent dataInterfaceServiceBindIntent = new Intent(context, DataInterfaceService.class); context.bindService(dataInterfaceServiceBindIntent, dataInterfaceServiceConnection, Context.BIND_AUTO_CREATE); // Wait until the dataInterface is set by the onServiceConnected() method. while (dataInterfaceServiceConnected == false) { Thread.sleep(spinInterval); } // Set the remote data model proxy on the data service switch (dataInterface.getLocalSettings().getRemoteDataModelProxyType()) { case SOAP: SetDataServiceRemoteDataModelProxy(dataInterface.getLocalSettings().getRemoteDataModelProxyType(), dataInterface.getLocalSettings().getSoapDataServiceLocation()); break; case REST: SetDataServiceRemoteDataModelProxy(dataInterface.getLocalSettings().getRemoteDataModelProxyType(), dataInterface.getLocalSettings().getRestDataServiceLocation()); break; default: throw new Exception("Unhandled " + RemoteDataModelProxyType.class.getName() + " field '" + dataInterface.getLocalSettings().getRemoteDataModelProxyType().name() + "' encountered."); } // Unbind the context of the object list activity from the service. // According to the Android documentation this should not be done, as the service could be stopped by the OS at any time after unbinding the activity from it // However if the service is actually stopped, this method will detect so and reconnect // Unbinding the activity from the service avoids problems with 'Activity has leaked ServiceConnection' exceptions when the activity is destroyed on orientation change or similar. context.unbindService(dataInterfaceServiceConnection); } }

There's a couple of undesirable (but unfortunately unavoidable) practices in this method. The first is the thread spinning while waiting for the Service to be started. This relates to the synchronous vs asynchronous paradigms discussed above. Android is primarily asynchronous in behaviour, however in this case, immediately after binding to the Service, I want to use methods in the data layer to set the protocol to use (SOAP or REST). Even in the absence of this requirement, the code calling the CheckDataInterfaceServiceConnection() method could want to call methods in the data layer at any time after binding to perform its basic function. Hence, I decided to spin wait until the 'dataInterfaceServiceConnected' flag is set (which is set when the DataInterfaceServiceConnection.onServiceConnected() method is called). As the CheckDataInterfaceServiceConnection() method is generally called from a worker thread (i.e. from the doInBackground() method of the ExceptionHandlingAsyncTask class), the thread spinning whilst not good practice, doesn't result in problems with 'Application Not Responding' dialogs.

The second point which could be improved is the call to Context.unbindService() at the end of the method. This means that immediately after starting the DataInterfaceService, there is nothing bound to it. I did because I experienced issues with 'Activity has leaked ServiceConnection' errors when closing the ObjectListActivity. In theory it means the Android OS could elect to shutdown the service at any time, although I never experienced issues with this in testing (however, granted I didn't do any testing with a device with high memory usage). In retrospect it probably would have been better to call the unbindService() method via the Presenter from the ObjectListActivity's onDestroy() method. I may make this change to the code in a future release.

SOAP and REST Implementation

As discussed above, I wanted to support the option of either SOAP or REST protocols to connect to the C# data layer. The diagram below shows how this dual support is implemented...

SOAP and REST Implementation

The DataInterfaceService exposes a setter method for a class implementing interface IRemoteDataModelProxy. Any of the DataInterfaceService public methods which need to access the C# webservice, are internally proxied to the IRemoteDataModelProxy class, which takes care of the actual data transport. There are 2 provided implementations of the IRemoteDataModelProxy interface, SoapRemoteDataModelProxy and RestRemoteDataModelProxy (facilitiating the connection to C# via SOAP and REST respectively).

Note that there are 2 parameters which are passed to all of the public C# webservice methods (in both SOAP and REST implementations)... the 'authenticationContext' and 'trackingData' parameters. 'authenticationContext' is used to pass user identification data to the webservice, and 'trackingData' is used to pass tracking information about the hosting device (location and network IP address). These parameters essentially hold metadata relating to each method call, and ideally should not be explicitly passed every time in the method signature. Particularly with the 'trackingData', a future improvement would be to use a more sophisticated authentication mechanism (connection-level or token based) which would eliminate the need to send it with every method call.

SOAP

The SoapRemoteDataModelProxy class uses the ksoap2 library to facilitate a connection to the C# webservice via SOAP. Most of the work is done in private method MakeSoapRequest()...

/** * Makes a SOAP web service request based on the supplied operation name and soap property parameters. * @param operationName The SOAP operation name (i.e. remote method to call). * @param parameters The parameters to pass to the method. * @param authenticationContext Authentication information to pass to the remote method. * @param trackingData Tracking information to pass to the remote method. * @return Object containing the response to the SOAP request * @throws IOException if an error occurs whilst making HTTP transport call as part of the SOAP request. * @throws org.xmlpull.v1.XmlPullParserException if an error occurs whilst making HTTP transport call as part of the SOAP request. * @throws org.json.JSONException if an error occurs whilst serializing the SOAP request parameters to JSON strings. */ private Object MakeSoapRequest(String operationName, ArrayList<SoapProperty> parameters, AuthenticationContext authenticationContext, TrackingData trackingData) throws IOException, XmlPullParserException, JSONException { // Create the ksoap2 utility objects if (instantiatedWithTestConstructor == false) { soapObject = new SoapObject(webServiceNamespace, operationName); envelope = new SoapSerializationEnvelope(SoapEnvelope.VER11); String httpTransportUrl = httpTransportUrlPrefix + dataModelLocation + httpTransportUrlPostfix; transport = new HttpTransportSE(httpTransportUrl); } // Add the parameters to the SoapObject for (SoapProperty currentSoapProperty : parameters) { soapObject.addProperty(currentSoapProperty.Name, currentSoapProperty.Value); } soapObject.addProperty("authenticationContext", jsonSerializer.SerializeAuthenticationContext(authenticationContext)); soapObject.addProperty("trackingData", jsonSerializer.SerializeTrackingData(trackingData)); // Create the SOAP envelope and add the parameters to it envelope.dotNet = true; envelope.implicitTypes = true; envelope.setOutputSoapObject(soapObject); // Execute the SOAP request transport.debug = true; transport.setXmlVersionTag(xmlVersionTag); transport.call(soapActionPrefix + operationName, envelope); return envelope.getResponse(); }

The following class diagram shows the relationships between classes and interfaces that expose the data layer of the application as a C# / WCF webservice.

Data Layer Webservice Class Diagram

Interface ISoapWebServiceApi uses WCF OperationContract attributes to define the methods that are exposed via SOAP, for example the GetMasterRoleToUserMapCollection() method which returns the complete list of role to use mappings defined in the data layer...

[OperationContract] String GetMasterRoleToUserMapCollection(String authenticationContext, String trackingData);

Note that although the implementation of this method in the underlying data layer returns a list of RoleToUserMap objects, in the ISoapWebServiceApi interface this method (and all other methods) return and accept just strings. As discussed above due to the limited support for XML serialization in Android, I decided to make the SOAP webservice handle JSON data types rather than XML, so passing all parameters as strings allows custom code to convert the strings to and from JSON objects. As an example, the implementation of the GetMasterRoleToUserMapCollection() method in the SoapWebServiceApi class, calls the WebServiceApiBase class to process the method, a List<RoleToUserMap> object is returned, and this is serialized to a JSON string...

public String GetMasterRoleToUserMapCollection(String authenticationContext, String trackingData) { List<RoleToUserMap> roleToUserMapList = base.GetMasterRoleToUserMapCollection(authenticationContext, trackingData); return jsonSerializer.Serialize(roleToUserMapList); }

REST

The Android RestRemoteDataModelProxy class facilitates connecting to the WCF webservice via REST. The Android API does not include a REST client library, and whilst there are several third party libraries available, I decided to create my own rest client code inside the RestRemoteDataModelProxy class by creating the relevant URLS, and using the HTTP functionality in the org.apache.http package. Private method CreateRestUrl() is used to create a REST compatible URL...

/** * Creates a valid REST URL using the given path segment, and including the specified query parameters in addition to the supplied AuthenticationContext and TrackingData as query parameters. * @param urlPathSegment The path to use in the URL (e.g. "Objects" in the URL "http://192.168.0.1:5000/OraclePermissionGeneratorWebServiceAPI/REST/Objects?"). * @param queryParameters A set of parameters to appear in the query portion of the URL. * @param authenticationContext Authentication information to include in the URL (as part of the query portion). * @param trackingData Tracking information to include in the URL (as part of the query portion). * @return The REST URL. * @throws JSONException if an error occurs whilst serializing the 'authenticationContext' and 'trackingData' parameters. */ private String CreateRestUrl(String urlPathSegment, ArrayList<RestQueryParameter> queryParameters, AuthenticationContext authenticationContext, TrackingData trackingData) throws JSONException { // Serialize parameters String serializedAuthenticationContext = jsonSerializer.SerializeAuthenticationContext(authenticationContext); String serializedTrackingData = jsonSerializer.SerializeTrackingData(trackingData); // Create the URL StringBuilder urlStringBuilder = new StringBuilder(512); urlStringBuilder.append(httpUrlPrefix); urlStringBuilder.append(dataModelLocation); urlStringBuilder.append(urlPathDelimiter); urlStringBuilder.append(urlBasePath); urlStringBuilder.append(urlPathDelimiter); urlStringBuilder.append(urlPathSegment); urlStringBuilder.append(urlQueryDelimiter); if (queryParameters.size() > 0) { for (RestQueryParameter currentParameter : queryParameters) { urlStringBuilder.append(currentParameter.Name); urlStringBuilder.append("="); urlStringBuilder.append(Uri.encode(currentParameter.Value)); urlStringBuilder.append(urlQueryParameterDelimiter); } } urlStringBuilder.append("authenticationContext="); urlStringBuilder.append(Uri.encode(serializedAuthenticationContext)); urlStringBuilder.append(urlQueryParameterDelimiter); urlStringBuilder.append("trackingData="); urlStringBuilder.append(Uri.encode(serializedTrackingData)); return urlStringBuilder.toString(); }

The various types of REST methods are called using private methods MakeGetRequest(), MakePutRequest(), MakePostRequest(), and MakeDeleteRequest(). The code for MakeGetRequest() appears below...

/** * Creates and sends a REST GET request. * @param urlPathSegment The path to use in the URL of the request (e.g. "Objects" in the URL "http://192.68.0.1:5000/OraclePermissionGeneratorWebServiceAPI/REST/Objects?"). * @param queryParameters A set of parameters to appear in the query portion of the URL of the request. * @param authenticationContext Authentication information to include in the URL of the request (as part of the query portion). * @param trackingData Tracking information to include in the URL of the request (as part of the query portion). * @return The response from REST request returned as a string. * @throws JSONException if an error occurs whilst serializing the 'authenticationContext' and 'trackingData' parameters. * @throws URISyntaxException if an error occurs when creating a URI. * @throws ClientProtocolException if a HTTP protocol error occurs when making the REST request. * @throws IOException if an error occurs or the connection was aborted when making the REST request. */ private String MakeGetRequest(String urlPathSegment, ArrayList<RestQueryParameter> queryParameters, AuthenticationContext authenticationContext, TrackingData trackingData) throws JSONException, URISyntaxException, ClientProtocolException, IOException { String url = CreateRestUrl(urlPathSegment, queryParameters, authenticationContext, trackingData); // Setup objects to create the GET request if (instantiatedWithTestConstructor == false) { httpClient = new DefaultHttpClient(); httpGet = new HttpGet(); } httpGet.setURI(new URI(url)); HttpResponse httpResponse = httpClient.execute(httpGet); return ConvertHttpResponseToString(httpResponse); }

On the C# side, the IRestWebServiceApi interface uses the OperationContract, WebGet, and WebInvoke attributes to define the methods exposed by REST, and how they are exposed. Through the WebGet and WebInvoke attributes, WCF can automatically handle serialization and deserialization of JSON parameters (via the RequestFormat and ResponseFormat properties) hence the RestWebServiceApi class which implements the interface doesn't require custom serialization code like the equivalent SOAP class does.

Designing the REST Interface

This project was my first experience with designing a REST API end to end. It requires a different approach to more traditional remote procedure call APIs, and there are many good web resources on building proper 'RESTful' APIs. I found this article on MSDN really useful, not least because it's specifically geared towards building REST webservices using WCF.

Moving from Verbs to Nouns - Probably the most fundamental concept to grasp when building a REST API is the change for having an unlimited number of 'verbs' to define, to being restricted to the verbs offered by HTTP and therefore by REST... i.e. GET, PUT, POST, DELETE, etc... For methods like GetRoles(), the REST implementation is straightforward... an HTTP GET method with a URL path '/Roles'. However, to implement methods like ObjectTypeValidate() requires a little more thought (here the verb 'validate' doesn't fit with the methods offered by HTTP). In this case I changed the paradigm from 'validating an object type' to 'getting a validation for an object type', and hence the method was implemented as an HTTP GET with a URL path '/Validations/ObjectType/[object type parameter]'. There were several cases where I had to adjust the 'perspective' of the data layer methods in this way, in order to get them to fit into the verbs available in REST.

WebInvoke.WebMessageBodyStyle Property - WCF exposes a property WebMessageBodyStyle on the WebInvoke attribute. For methods with JSON parameters, this attribute defines whether the parameters in the request and/or response messages should be wrapped with JSON notation. For example the GetDefaultObjectOwner() method with the default 'Bare' WebMessageBodyStyle returns the default object owner as a raw string...

"XYZON"

...however setting BodyStyle = WebMessageBodyStyle.WrappedResponse wraps the returned string in JSON notation as follows...

{"GetDefaultObjectOwnerResult":"XYZON"}

At runtime, WCF tries to map the parameters of the method to parameters defined in the UriTemplate property of the WebInvoke attribute. Any parameters which do not map are assumed to be part of the request body. For example the AddRoleToUserMap() method has the following definition...

[OperationContract] [WebInvoke(UriTemplate = "RoleToUserMappings?authenticationContext={authenticationContext}&trackingData={trackingData}", RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json, Method = "POST", BodyStyle = WebMessageBodyStyle.WrappedRequest)] void AddRoleToUserMap(String role, String user, String authenticationContext, String trackingData);

Both the 'authenticationContext' and 'trackingData' parameters are defined in the UriTemplate, leaving the 'role' and 'user' parameters to be included in the message body. However, using the default 'Bare' WebMessageBodyStyle means there is no mechanism of differentiate between the two parameters in the message (having a single parameter in the body is allowed using the 'Bare' style, but not any more), and will result in the following exception...

InvalidOperationException Operation '[method name]' of contract '[interface name]' specifies multiple request body parameters to be serialized without any wrapper elements. At most one body parameter can be serialized without wrapper elements. Either remove the extra body parameters or set the BodyStyle property on the WebGetAttribute/WebInvokeAttribute to Wrapped.

Hence for methods like AddRoleToUserMap() with multiple body parameters, the BodyStyle must be set as wrapped. An example of correctly wrapped message body parameters for the AddRoleToUserMap() method appears below...

{"user":"XYZON_READ_ROLE","role":"NEW_USER"}

Correct HTTP Headers for PUT and POST Methods - For PUT and POST methods to be properly processed by WCF, the following HTTP header string must by set on the request messages...

'Content-Type:"text/json; charset=UTF-8"'

This is implemented in the MakePutRequest() and MakePostRequest() methods in the Android RestRemoteDataModelProxy class...

httpPost.setHeader(httpHeaderName, httpHeaderValue);

DELETE Methods and Idempotency - According to the strict definition of REST, DELETE methods should be idempotent, meaning that if for example a DELETE method is called to remove an entity that has already been removed or doesn't exist, the method should not return an error. However, the data layer of the original Windows Forms version of the application was designed to throw an exception in such cases (e.g. if the RemoveRoleToUserMap() method was called for a role to user mapping which didn't exist). Rather than override the original functionality I chose to leave it throw an exception in these cases, but it does mean that the REST interface doesn't strictly follow REST principles in this regard.

POST vs PUT for Inserts - The HTTP PUT method is designed to be used to create or update a resource. The AddRoleToUserMap() method in the C# webservice is used to create a role to user mapping, and hence by this definition should use the PUT method. However, PUT method calls are supposed to be idempotent (as for DELETE methods described above), and the AddRoleToUserMap() will return an error if called with a mapping which already exists. Rather than break REST principles by using the PUT method, I elected instead to use the POST method, which is design for more generic usage and does not need to be idempotent. The same applies to the AddObjectPermissionSet() and AddPermission() methods.

Sending 'authenticationContext' and 'trackingData' as Query Parameters - Any parameters which are defined in the UriTemplate property have to be of type string. The 'authenticationContext' and 'trackingData' parameters are complex container objects, and are passed with every method call to provide user identification and tracking data associated with the device making the webservice request. I had to think about the correct way to include these parameters in the REST interface. Rather than being part of the application data model, they are really just metadata passed with each method call. Hence I didn't think they belonged in the path section of the UriTemplate, which is more used for entities which are part of the data model (like '/Validations/ObjectName/' or '/PrivilegeScripts/Rollout'). I decided instead to put them in the query parameters section of the UriTemplate as this is typically used for optional or additional information in REST interfaces. However to comply with the restriction that parameters in the UriTemplate must be strings, both parameters are defined in the method signatures as strings, but are explicitly deserialized to the relevant objects in the WebServiceApiBase class.

REST Method Definitions - This page contains a full list of the public methods in the data layer, and their corresponding REST implementation.

Exposing SOAP and REST

To expose the data layer through SOAP and REST webservices simultaneously, I create 2 instances of the ServiceHost class, which expose the methods defined in the SoapWebServiceApi and RestWebServiceApi classes (and hence the ISoapWebServiceApi and IRestWebServiceApi interfaces) via WCF. So that both webservices act on the same data layer instance, the Dictionary of OraclePermissionGeneratorDataInterfaceLayer objects (explained above) is injected into the constructors of both WebServiceApi classes.

using (FileTrackingDataLogger trackingDataLogger = new FileTrackingDataLogger(@"C:\Temp\")) { Dictionary<string, OraclePermissionGeneratorDataInterfaceLayer> userDataRespository = new Dictionary<string, OraclePermissionGeneratorDataInterfaceLayer>(); SoapWebServiceApi soapApiInstance = new SoapWebServiceApi(userDataRespository, trackingDataLogger); RestWebServiceApi restApiInstance = new RestWebServiceApi(userDataRespository, trackingDataLogger); soapApiInstance.AddUser("tutorial_user@tempuri.org"); soapApiInstance.LoadDataModelFromFile("tutorial_user@tempuri.org", @"..\..\..\Resources\Tutorial 2.xml", @"..\..\..\Resources\ORACLE_PERMISSION_GENERATOR_CONFIG.xsd"); using (ServiceHost soapServiceHost = new ServiceHost(soapApiInstance, soapBaseAddress)) using (ServiceHost restServiceHost = new ServiceHost(restApiInstance, restBaseAddress)) { // Set SOAP service host settings ServiceMetadataBehavior soapServiceMetadataBehavior = new ServiceMetadataBehavior(); (etc...)

One caveat with using 2 ServiceHost instances, is that the SOAP and REST webservices have to listen on different ports (in this case 5000 and 5001). If required it would be possible to expose both SOAP and REST on a single ServiceHost instance and port. However, separate method signatures for SOAP and REST would have to be defined in a single interface, e.g. something like the following...

[ServiceContract] public interface IWebServiceApi { // SOAP method definition [OperationContract] void AddRoleToUserMap(String role, String user, String authenticationContext, String trackingData); // REST method definition [OperationContract] [WebInvoke(UriTemplate = "RoleToUserMappings?authenticationContext={authenticationContext}&trackingData={trackingData}", RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json, Method = "POST", BodyStyle = WebMessageBodyStyle.WrappedRequest)] void AddRoleToUserMapRest(String role, String user, String authenticationContext, String trackingData); (etc...)

Notice the REST version of the method has to include the 'Rest' suffix to prevent a definition clash with the SOAP version. In my case I preferred to maintain cleaner method names at the expense of having to run the webservices on separate ports, but if running on a single port was a requirement the above technique could be used.

Comparing SOAP and REST

Implementing the same API using both SOAP and REST gave a good opportunity to compare the details of each protocol side by side. The message samples below show the raw HTTP request and response messages sent by SOAP and REST for the RoleToUserMapValidate() method...

SOAP Request

POST /OraclePermissionGeneratorWebServiceAPI/SOAP HTTP/1.1 user-agent: ksoap2-android/2.6.0+ soapaction: http://tempuri.org/ISoapWebServiceApi/RoleToUserMapValidate content-type: text/xml;charset=utf-8 accept-encoding: gzip content-length: 585 Host: 169.254.185.88:5000 Connection: Keep-Alive <!--?xml version="1.0" encoding= "UTF-8" ?--> <v:Envelope xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns:d="http://www.w3.org/2001/XMLSchema" xmlns:c="http://schemas.xmlsoap.org/soap/encoding/" xmlns:v="http://schemas.xmlsoap.org/soap/envelope/"> <v:Header /> <v:Body> <RoleToUserMapValidate xmlns="http://tempuri.org/" id="o0" c:root="1"> <role>NEW_ROLE</role> <user>NEW_USER</user> <authenticationContext>{"UserIdentifier":"tutorial_user@tempuri.org"}</authenticationContext> <trackingData>{"IpV4Address":[10,0,2,15]}</trackingData> </RoleToUserMapValidate> </v:Body> </v:Envelope>

SOAP Response

HTTP/1.1 200 OK Content-Length: 281 Content-Type: text/xml; charset=utf-8 Server: Microsoft-HTTPAPI/1.0 Date: Fri, 25 Dec 2015 00:20:51 GMT <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"> <s:Body> <RoleToUserMapValidateResponse xmlns="http://tempuri.org/"> <RoleToUserMapValidateResult>{"IsValid":true,"ValidationError":""}</RoleToUserMapValidateResult> </RoleToUserMapValidateResponse> </s:Body> </s:Envelope>

REST Request

GET /OraclePermissionGeneratorWebServiceAPI/REST/Validations/RoleToUserMap/NEW_ROLE/NEW_USER?authenticationContext=%7B%22UserIdentifier%22%3A%22tutorial_user%40tempuri.org%22%7D&trackingData=%7B%22IpV4Address%22%3A%5B10%2C0%2C2%2C15%5D%7D HTTP/1.1 Host: 1 Connection: Keep-Alive User-Agent: Apache-HttpClient/UNAVAILABLE (java 1.4)

REST Response

HTTP/1.1 200 OK Content-Length: 37 Content-Type: application/json; charset=utf-8 Server: Microsoft-HTTPAPI/1.0 Date: Fri, 25 Dec 2015 00:41:58 GMT {"IsValid":true,"ValidationError":""}

End to End Sample Implementation Using MVP

In this section I'll run through the implementation of a simple Activity in Android end-to-end using the MVP pattern. I'll use the AddRoleToUserMapActivity Activity as an example as its interaction with the Presenter and Model layers is outlined in the sequence diagram discussed earlier. The Activity allows the user to define a new mapping between an Oracle database role and user account, and click a button to add that mapping to the data layer...

Add Role to User Map Activity

The diagram below shows all classes which must be created or modified in order to implement the Activity's functionality end to end, and the layer of the MVP pattern to which they belong...

Add Role to User Map Activity End to End Implementation

C# Webservices

The Activity needs to call 2 methods in the data layer... RoleToUserMapValidate() and AddRoleToUserMap(). These methods must be defined in the ISoapWebServiceApi and IRestWebServiceApi interfaces...

[ServiceContract] public interface IRestWebServiceApi { [OperationContract] [WebGet(UriTemplate = "/Validations/RoleToUserMap/{role}/{user}?authenticationContext={authenticationContext}&trackingData={trackingData}", RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)] Containers.ValidationResult RoleToUserMapValidate(String role, String user, String authenticationContext, String trackingData); [OperationContract] [WebInvoke(UriTemplate = "RoleToUserMappings?authenticationContext={authenticationContext}&trackingData={trackingData}", RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json, Method = "POST", BodyStyle = WebMessageBodyStyle.WrappedRequest)] void AddRoleToUserMap(String role, String user, String authenticationContext, String trackingData); (etc...) [ServiceContract] public interface ISoapWebServiceApi { [OperationContract] String RoleToUserMapValidate(String role, String user, String authenticationContext, String trackingData); [OperationContract] void AddRoleToUserMap(String role, String user, String authenticationContext, String trackingData); (etc...)

The methods should be implemented in the SoapWebServiceApi and RestWebServiceApi classes, which simply proxy the methods calls to the WebServiceApiBase class. The WebServiceApiBase class then calls the relevant methods in the OraclePermissionGeneratorDataInterfaceLayer class from the original Windows Forms version of the application...

public virtual Containers.ValidationResult RoleToUserMapValidate(String role, String user, String authenticationContext, String trackingData) { // Deserialize parameters AuthenticationContext deserializedAuthenticationContext = jsonSerializer.DeserializeAuthenticationContext(authenticationContext); // Call data layer method ValidateUser(deserializedAuthenticationContext.UserIdentifier); OraclePermissionGeneratorDataModel.ValidationResult validationResult = userDataRepository[deserializedAuthenticationContext.UserIdentifier].RoleToUserMapValidate(role, user); Containers.ValidationResult returnValidationResult = containerObjectConverter.Convert(validationResult); LogTrackingData(deserializedAuthenticationContext.UserIdentifier, "RoleToUserMapValidate(role, user)", trackingData); return returnValidationResult; } public virtual void AddRoleToUserMap(String role, String user, String authenticationContext, String trackingData) { // Deserialize parameters AuthenticationContext deserializedAuthenticationContext = jsonSerializer.DeserializeAuthenticationContext(authenticationContext); // Call data layer method ValidateUser(deserializedAuthenticationContext.UserIdentifier); userDataRepository[deserializedAuthenticationContext.UserIdentifier].AddRoleToUserMap(role, user); LogTrackingData(deserializedAuthenticationContext.UserIdentifier, "AddRoleToUserMap(role, user)", trackingData); }

Android DataInterfaceService class (MVP 'Model')

The 2 methods need to be available in the Android-side DataInterfaceService class. As the implementation of these methods is provided to the DataInterfaceService class by the SoapRemoteDataModelProxy and RestRemoteDataModelProxy classes, the two methods must be implemented in each of these classes...

public class SoapRemoteDataModelProxy implements IRemoteDataModelProxy { @Override public ValidationResult RoleToUserMapValidate(String role, String user, AuthenticationContext authenticationContext, TrackingData trackingData) throws Exception { ArrayList<SoapProperty> parameters = new ArrayList<SoapProperty>(); parameters.add(new SoapProperty("role", role)); parameters.add(new SoapProperty("user", user)); Object result = MakeSoapRequest("RoleToUserMapValidate", parameters, authenticationContext, trackingData); // Deserialize SOAP response ValidationResult validationResult = jsonSerializer.DeserializeValidationResult(result.toString()); return validationResult; } @Override public void AddRoleToUserMap(String role, String user, AuthenticationContext authenticationContext, TrackingData trackingData) throws Exception { ArrayList<SoapProperty> parameters = new ArrayList<SoapProperty>(); parameters.add(new SoapProperty("role", role)); parameters.add(new SoapProperty("user", user)); MakeSoapRequest("AddRoleToUserMap", parameters, authenticationContext, trackingData); } (etc...) public class RestRemoteDataModelProxy implements IRemoteDataModelProxy { @Override public ValidationResult RoleToUserMapValidate(String role, String user, AuthenticationContext authenticationContext, TrackingData trackingData) throws Exception { StringBuilder urlPathSegment = new StringBuilder(); urlPathSegment.append("Validations"); urlPathSegment.append(urlPathDelimiter); urlPathSegment.append("RoleToUserMap"); urlPathSegment.append(urlPathDelimiter); urlPathSegment.append(Uri.encode(role)); urlPathSegment.append(urlPathDelimiter); urlPathSegment.append(Uri.encode(user)); String response = MakeGetRequest(urlPathSegment.toString(), new ArrayList<RestQueryParameter>(), authenticationContext, trackingData); return jsonSerializer.DeserializeValidationResult(response); } @Override public void AddRoleToUserMap(String role, String user, AuthenticationContext authenticationContext, TrackingData trackingData) throws Exception { JSONObject parameters = new JSONObject(); parameters.put("role", role); parameters.put("user", user); MakePostRequest(parameters.toString(), "RoleToUserMappings", new ArrayList<RestQueryParameter>(), authenticationContext, trackingData); } (etc...)

AddRoleToUserMapActivity class (MVP 'View')

The layout of user interface items (fields, labels, buttons, etc...) in an Android Activity are defined through an XML file in the res/layout/ folder of the Android project. I won't go into detail of the content of the XML here, as there are many good online resources on the subject (and it's also possible to build the Activity using a WYSIWYG designer in Eclipse). However a small sample of the layout for the AddRoleToUserMapActivity appears below...

<?xml version="1.0" encoding="utf-8"?> <!-- The layout for the add role to user map activity, used to add new role to user mappings --> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:background="@color/list_title_bar_background" > <TextView android:id="@+id/add_role_to_user_map_header" style="@style/ActivityTitleBar" android:text="@string/add_role_to_user_map_header_text" > </TextView> (etc...)

The AddRoleToUserMapActivity Java code includes a handler for the 'Add Mapping' button, and importantly establishes a link to the Presenter class in the onCreate() method. This method also contains code to setup the appearance of the Activity according to the previously defined XML layout file (by calling method setContentView())...

@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.add_role_to_user_map_activity); presenter = PresenterGlobalStorer.getPresenter(); presenter.setAddRoleToUserMapView(this); }

Note that this Activity implements interface IAddRoleToUserMapView, so that it can easily be mocked when unit testing the presenter. In following the paradigm of MVP, all views in the application (and hence all Activities) should implement an interface.

Presenter class (MVP 'Presenter')

After implementing the Model and View components of the pattern, the Presenter acts as the 'glue' betweeen these layers. It must perform two functions. The first is to allow a mechanism to display the AddRoleToUserMapActivity. This is implemented in the ShowAddRoleToUserMapView() method...

@Override public void ShowAddRoleToUserMapView() { Context roleToUserMapViewContext = (Context)roleToUserMapView; Intent showAddRoleToUserMapViewIntent = new Intent(roleToUserMapViewContext, AddRoleToUserMapActivity.class); roleToUserMapViewContext.startActivity(showAddRoleToUserMapViewIntent); }

As this method doesn't require any access to the data layer (or other long running operation), it doesn't need to use the ASyncTask class, and simply opens the AddRoleToUserMapActivity via an Intent.

The second function is to handle when the user clicks the 'Add Mapping' button in the AddRoleToUserMapActivity. This is handled by Presenter method AddRoleToUserMap(), and as the method needs to access the data layer, it uses the ASyncTask class to do so. In an instance of the ExceptionHandlingAsyncTask class, the doInBackground() method is overridden to validate the role and user using the DataInterfaceService.RoleToUserMapValidate() method. If the validation returns as successful, the DataInterfaceService.AddRoleToUserMap() method is called to add the new mapping, and regardless of success or failure the result of the validation is sent back to the main application thread...

@Override protected ValidationResultContainer<String[]> doInBackground(String... parameters) { ValidationResult validationResult = null; try { CheckDataInterfaceServiceConnection(); validationResult = dataInterface.RoleToUserMapValidate(parameters[0], parameters[1]); if (validationResult.getIsValid() == false) { return new ValidationResultContainer<String[]>(validationResult, new String[] { parameters[0], parameters[1] }); } dataInterface.AddRoleToUserMap(parameters[0], parameters[1]); } catch (Exception e) { doInBackgroundException = e; } return new ValidationResultContainer<String[]>(validationResult, new String[] { parameters[0], parameters[1] }); }

The subsequent action on the main application thread is defined by overriding the ExceptionHandlingAsyncTask.onPostExecute() method. If the validation was successful, the new role to user mapping is added to the RoleToUserMapActivity (which is the parent Activity of AddRoleToUserMapActivity and will be open behind it), and then the AddRoleToUserMapActivity is closed. If the validation failed, an alert dialog is displayed, showing the reason for the failure...

@Override protected void onPostExecute(ValidationResultContainer<String[]> validationResultContainer) { if (HandleBackgroundException() == false) { if (validationResultContainer.ValidationResult.getIsValid() == true) { // Add the mapping to the role to user map view RoleToUserMap newRoleToUserMap = new RoleToUserMap(validationResultContainer.PostExecuteParameter[0], validationResultContainer.PostExecuteParameter[1]); roleToUserMapView.AddRoleToUserMap(newRoleToUserMap); notificationDialogDisplayView.Close(); } else { // Show an error dialog with the results of the validation // Create a class to define the action to take when the user clicks the 'OK' button on the alert dialog OnClickListener alertDialogConfirmationAction = new OnClickListener() { @Override public void onClick(DialogInterface arg0, int arg1) { // Close the alert dialog notificationDialogDisplayView.CloseOkDialog(); } }; notificationDialogDisplayView.ShowOkDialog("Error", validationResultContainer.ValidationResult.getValidationError(), alertDialogConfirmationAction); } } super.onPostExecute(validationResultContainer); }

Unit Tests

I implemented fairly extensive unit tests for the project using the typical unit testing paradigm for MVP applications... i.e. tests are included for the Presenter and Model components, whilst the Views are kept simple and hence don't require unit tests. Unit testing for Android, at the time I started this project, had a few differences from and disadvantages compared to unit testing of a standard Java project...

  • Unit tests ran on JUnit 3 rather than JUnit 4, meaning that some syntax was different, and features of JUnit 4 were not available.
  • Tests which depended on packages in the Android API must be executed on an Android emulator. To facilitate this, the tests are actually built as an Android application, and installed and run on the emulator. The extra overhead in this process means that Android unit tests can be significantly slower to run than standard Java tests.
  • Mocking of classes and interfaces is possible with Mockito, provided version 1.10.5 or above is used. Also Google dexmaker must be included in the test project to allow tests using Mockito to be compiled into Android-compatible Java bytecode.

That said, more recently Android unit tests have improved some of these earlier problems, and have been better integrated into Android Studio including...

Sample Test

The success test for the Presenter.AddRoleToUserMap() method is shown below. As the Presenter manages worker threads via the ASyncTask class, a mechanism was needed to ensure that the worker thread tasks were completed before verifications and assertions were performed in the tests. To facilitate this a 'test' constructor is defined on the Presenter, which allows injecting of a CountDownLatch object...

/** * Initialises a new instance of the Presenter class. * <b>Note</b> this is an additional constructor to facilitate unit tests, and should not be used to instantiate the class under normal conditions. * @param dataInterface A test (mock) data interface. * @param exceptionLogger A test (mock) exception logger. * @param backgroundThreadCompleteSignal Notifies test code that a background worker thread process has completed (so that assertions can subsequently be made in units tests). */ public Presenter(IDataInterface dataInterface, IExceptionLogger exceptionLogger, CountDownLatch backgroundThreadCompleteSignal) { this.dataInterface = dataInterface; this.exceptionLogger = exceptionLogger; this.backgroundThreadCompleteSignal = backgroundThreadCompleteSignal; instantiatedWithTestConstructor = true; dataInterfaceServiceConnected = true; }

In the test code, the await() method is called on this CountDownLatch object before asserting the results of the test...

public void testAddRoleToUserMapSuccessTest() throws Throwable { final String testRole = "XYZON_POWER_ROLE"; final String testUser = "XYZON_POWER_USER"; when(mockDataInterface.RoleToUserMapValidate(testRole, testUser)).thenReturn(new ValidationResult(true, "")); runTestOnUiThread(new Runnable() { @Override public void run() { testPresenter.AddRoleToUserMap(testRole, testUser); } }); backgroundThreadCompleteSignal.await(); verify(mockAddRoleToUserMapView).ShowWaitDialog("Please Wait"), "Retrieving data..."); verify(mockDataInterface).RoleToUserMapValidate(testRole, testUser); verify(mockDataInterface).AddRoleToUserMap(testRole, testUser); verify(mockRoleToUserMapView).AddRoleToUserMap(argThat(new RoleToUserMapMatcher(new RoleToUserMap(testRole, testUser)))); verify(mockAddRoleToUserMapView).Close(); verify(mockAddRoleToUserMapView).CloseWaitDialog(); verifyNoMoreInteractions(allMocks); }

Note that the unit tests are split between two projects... 'OraclePermissionGenerator.AndroidUnitTests' and 'OraclePermissionGenerator.UnitTests'. The former tests classes which have a dependence on the Android API, and hence must be run through Eclipse as 'Android JUnit Tests'. The plain 'UnitTests' project contains classes which contain just standard Java code, and hence can be run as 'JUnit Tests' (and hence run much more quickly).

User Behaviour Tracking

Tracking information containing the Android host device's location an IP address is passed to the C# webservice with every method call. By default, the webservice logs this information to a text file using the FileTrackingDataLogger class. Additionally, since version 0.10.0.0 of OPG Android classes are available to log this information to either a DynamoDB or Redshift instance on Amazon Web Services.

This page explains the setup process and prerequisites for using DynamoDB and Redshift.

To log to DynamoDB (via the AwsDynamoDbTrackingDataLogger class), change the initialization section of the C# Program class as follows...

//using (FileTrackingDataLogger trackingDataLogger = new FileTrackingDataLogger(@"C:\Temp\")) using (AwsDynamoDbTrackingDataLogger trackingDataLogger = new AwsDynamoDbTrackingDataLogger("[AWS IAM access key id]", "[AWS IAM secret access key]")) { Dictionary<string, OraclePermissionGeneratorDataInterfaceLayer> userDataRespository = new Dictionary<string, OraclePermissionGeneratorDataInterfaceLayer>(); SoapWebServiceApi soapApiInstance = new SoapWebServiceApi(userDataRespository, trackingDataLogger); RestWebServiceApi restApiInstance = new RestWebServiceApi(userDataRespository, trackingDataLogger);

... or for Redshift, use the following code...

//using (FileTrackingDataLogger trackingDataLogger = new FileTrackingDataLogger(@"C:\Temp\")) using (AwsRedshiftTrackingDataLogger trackingDataLogger = new AwsRedshiftTrackingDataLogger("[redshift server location]", 5439, "[redshift server instance name]", "[user name]", "[password]")) { trackingDataLogger.Connect(); Dictionary<string, OraclePermissionGeneratorDataInterfaceLayer> userDataRespository = new Dictionary<string, OraclePermissionGeneratorDataInterfaceLayer>(); SoapWebServiceApi soapApiInstance = new SoapWebServiceApi(userDataRespository, trackingDataLogger); RestWebServiceApi restApiInstance = new RestWebServiceApi(userDataRespository, trackingDataLogger); ... trackingDataLogger.Disconnect(); }

Note that the AwsRedshiftTrackingDataLogger class needs to be explicitly connected and then disconnected from the Redshift database instance.

Both classes access the relevant AWS service as part of the webservice method call, which is fine for a prototype case. However in real production code, the tracking data should be queued and sent to AWS in batches (by a separate thread, process, or server), to minimize the webservice's response time to a method call.

Additional Items

There are a few fundamental Android concepts and components I used, which are well documented in Android's documentation and other online tutorials. These are listed below with a reference to the classes or components within OPG Android which show an example of them...

  • ListViews - These are very common subclasses of View in Android which are used to show scrollable lists of similar items in an application. The SelectRoleActivity shows a basic example of this class (including binding to the data backing the list using the StringArrayAdapter class).
  • Styles - Styles allow you to define common visual styles for UI elements, to achieve visual consistency between Activities, and prevent duplication and re-defining of layout XML. I define common styles for the Activity title bar, input fields, buttons, and ListViews in the res/values/styles.xml file.

The activitytest Package

After having successfully finished OPG Android using MVP (and having had to workaround some of the problems I faced in doing so), I was interested in seeing my approach and code compared to implementing the same functionality using the more commonly advocated method of putting the MVP Presenter functionality directly in each Activity class. I created the net.alastairwyse.oraclepermissiongenerator.activitytest package, to reimplement the RoleToUserMapActivity without using the Presenter and compare the result to the MVP version in terms of separation of concerns, ease of unit testing, etc... This is currently a work in progress.

In Hindsight

After having completed the application using the MVP pattern, looking back there are a few alternative approaches which would have yielded a different result, and could be further investigated and compared to the current design...

  • Could MVP have been better implemented, putting the presenter code inside each Activity, and having multiple presenters (effectively one Presenter for each View)? This might have ended up being inline with the more commonly advocated design of having the Presenter code inside the Activity, but potentially with greater logical separation of View and Presenter-related code within the Activities.
  • To avoid the issues with getting a reference to the Presenter from newly started Activities, could the Presenter have been implemented as an Android Service, in the same way the Model / data layer was?

Future Improvements

  • Make the .NET data layer thread safe
  • Implement proper authentication and session management in the webservices. It may be possible to use HMAC as outlined in this article. This may also mean that sending of the 'authenticationContext' parameter with every webservice method could be avoided.
  • The current version of OPG Android has only been tested on limited screen resolutions (basically on a 480x800 emulator). An improvement would be to allow it to properly adapt to multiple screen resolutions.
  • My implementation of retrieving the location of the device hosting the application, uses the android.location.LocationManager class. This is since been superseded and simplified by the Location Services API offerred by Google Play Services.
  • The application should be properly tested for handling multiple charactersets and characters outside the ANSI set. For example, the RestRemoteDataModelProxy class uses default UTF-8 encoding when creating REST URIs.
  • REST webservice methods which perform validation of values (e.g. RoleToUserMapValidate()) will fail attempting to validate 0 length parameters. The reason for this is that the 0 length parameter prevents WCF from properly matching the REST URI to a method in the IRestWebServiceApi interface. For example, the path portion of the URI for this method for >0 length parameters would be '/Validations/RoleToUserMap/NEW_ROLE/NEW_USER?'... if the role was 0 length, the path would become '/Validations/RoleToUserMap//NEW_USER?', which fundamentally changes the structure of the URI. This could be be overcome by putting the parameters into the query section of the URI or message body (neither of which are ideal in terms of conforming to REST), or potentially using a 'special' string value to represent a blank string.
  • Properly cache an Activity's data and state, to ensure both are persisted when the orientation of the Activity is changed. The Android API offers several mechanisms to assist this, for example overriding the onSaveInstanceState() method in an Activity.
  • Reduce the 'chattiness' of the webservice to reduce network communication and hence increase device battery life (e.g. combine validation method calls and their subsequent actions on success into a single network roundtrip)
  • Have the ability for the application to work in a disconnected state (i.e. implement client-side caching of operations and subsequent re-synchronization with the webservice).
  • Re-implement Activities using a more asynchronous paradigm... i.e. don't block the user from the UI when accessing the data layer.
  • Implement 'lazy' population of ListViews. Currently ListViews (like on the main ObjectListActivity) are immediately populated with the full list of items backing them from the data layer. This works fine for small numbers of items, but gets noticeably slow with very large sets (e.g. when the ObjectListActivity contains 100's of objects).
  • Greater testing of boundary scenarios with the Activity lifecycle. As a simple example, what would happen if the RoleToUserMapActivity was closed by Android when the user was working in the AddRoleToUserMapActivity. If this occurred it would potentially result in NullPointerExceptions when the Presenter attempted to update the RoleToUserMapActivity after adding a new role to user mapping. To make the application production ready these types of scenarios would need to be fully investigated and tested.
  • Upgrade the project to Android Studio

Links

The source code for the project is available on GitHub.

JavaDoc documentation for the Android Java code.

Lars Vogel's Android Tutorials - these provide an excellent practical reference for many of the fundamental concepts regarding building Android applications.

Android REST client applications from Google I/O 2010. A little bit dated now, but suggests some patterns for implementing asynchronous/cached connections to REST webservices.

I presented many of the ideas from this page to colleagues in a series of training sessions. The powerpoint presentations from these sessions are available here and here.

Contact

Feel free to contact me via the details here with any questions or comments.