High-availability mobile applications

Mobile databases and J2ME tools

Today's wireless networks are notoriously unreliable. Unavailable applications destroy the core value of mobile commerce, which is the promise of accessing information anytime, anywhere. In fact, the requirement of always-on connections is one of the major reasons why Wireless Application Protocol (WAP)-based thin-client mobile commerce failed to take off. Mobile commerce requires smart, mobile clients that can handle applications locally and continue to function even when the network is temporarily unavailable (i.e., offline operations). The "occasionally connected" mobile applications require advanced mobile data storage and management tools. Such tools are readily available on J2ME (Java 2 Platform, Micro Edition).

In this article, I first discuss general concepts and specifications of mobile databases. I use an example application to illustrate important designs and key components of J2ME mobile database applications. I also provide useful advice on how to choose the right mobile database in today's diverse market.

On-device data storage and management

The first requirement for offline operations is to store and manage application data on the device itself. The standard J2ME profiles provide only limited data storage and management capabilities. On low-end MIDP (Mobile Information Device Profile) phones, we have only linear record stores from the record management system (RMS); in the Foundation Profile (or PersonalJava), we have plain random files. The application developer must organize data using those generic facilities, which can prove tedious, inefficient, and error prone for large applications.

In the J2SE (Java 2 Platform, Standard Edition) and J2EE (Java 2 Platform, Enterprise Edition) worlds, the relational database represents the key technology for data management. Most Java developers are already familiar with common database access APIs such as JDBC (Java DataBase Connectivity). A lightweight relational database is therefore our natural choice for mobile data management.

In addition to supporting offline operation modes, on-device databases have other important benefits:

  • They can manage user preferences. Extreme personalization is touted as a major benefit of mobile commerce.
  • They can provide a performance cache to reduce network roundtrips, which could drastically improve application performance over today's slow and long latency wireless networks.

The JDBC optional package

The standard JDBC API has always been optionally available for high-end Java mobile devices. PersonalJava runtimes are often bundled with the optional JDBC 1.x API. PersonalJava's successor, the J2ME Foundation/Personal Profile (FP/PP), does not mandate JDBC. Instead, a rich subset of the JDBC 3.x API is made available to FP/PP devices through the J2ME JDBC optional package (JDBC OP). JSR (Java Specification Request) 169 is developing this optional package's specification; the final draft was proposed in November 2002. The JDBC OP supports most commonly used JDBC features except the following:

  • Connection pools
  • The ParameterMetaData interface
  • Setting parameters for stored procedures by name in the CallableStatement interface
  • SQL 99 types (the Struct, Array, Ref, SQLData, SQLInput, and SQLOutput interfaces)
  • Custom type mapping (the setTypeMap() and getTypeMap() methods)

Data synchronization

Another important aspect of the occasionally connected application paradigm is data synchronization. Standalone mobile databases are isolated pockets of data. Mobile applications are much more useful when we connect those islands with powerful backend servers. Connected mobile databases have the following advantages:

  • A mobile client often needs to access enterprise IT infrastructure. For example, a mobile sales application requires updated inventory data.
  • A backend application needs up-to-date and aggregated data to make intelligent business decisions or generate accurate reports. For example, a supply chain application needs to aggregate data from mobile sales people before deciding how much it should order from suppliers.
  • The back end enables information sharing among mobile peers.

As a result, smart clients should support not only offline management of on-device data, but also data synchronization with backend databases when the network is available. The added bonus of a synchronized solution is that the backend database can act as a content provisioning repository for simplifying mobile device management.

In theory, the application developer can handle the entire synchronization logic. However, developing an optimized, secure, and scalable synchronization solution requires much expertise. Mobile database products normally come with proprietary synchronization tools and APIs to take away the pain from developers.

What about SyncML?

A popular application-level synchronization protocol is SyncML. Besides generic client application platforms such as J2ME, mobile devices often come preinstalled with different sets of native smart applications. Examples include calendars, address books, and contact lists (personal information management, or PIM, applications) on smart phone or PDA devices. Those applications often synchronize data with desktop PCs or other central data repositories through proprietary protocols supported by vendor-supplied driver software. The incompatible protocols have created many problems for users. For example, synchronizing a Pocket PC device with a Mac or Linux desktop is difficult; if a person owns multiple mobile devices, he must install multiple drivers that could potentially conflict. Users demand a standard synchronization protocol that allows any device to synchronize with any backend application without proprietary driver applications.

SyncML is a standard XML data format that defines the syntax to describe simple PIM data such as vCard and vCalendar. SyncML can access database-powered enterprise backend information systems. For example, IBM WebSphere Everyplace Access provides SyncML clients access to Lotus Notes and Microsoft Exchange servers. Important enterprise databases such as IBM DB2 and Oracle9i Database have built-in SyncML support. Although SyncML is great for PIM-type applications, it is not powerful enough to synchronize generic relational databases; thus, this article does not focus on SyncML.

An example application

Now, through a simple example, we examine typical usage scenarios and key components of mobile database applications.

Mobile contact manager in action

The example is a mobile contact manager application provided by PointBase. ContactManager is included in PointBase 4.x distributions. For your convenience, I have included the source code in a download zip file in Resources. If you want to build and run the examples yourself, you still must download the appropriate jar files from PointBase.

The application itself is simple: it mainly duplicates features commonly found in advanced address book applications. For example, it allows the user to store contact name, address, and phone numbers with pictures; provides intuitive browsing and searching interfaces; and synchronizes with backend database servers. Figures 1 and 2 demonstrate the application in action in the standalone mode and synchronized mode, respectively. The screenshots are from a Pocket PC powered by Insignia's Jeode PersonalJava VM and a Mac OS X laptop powered by J2SE. The same byte-code application runs without modification on many platforms, demonstrating Java's power.

Figure 1. Standalone ContactManager in action on Pocket PC Jeode PersonalJava
Figure 2. Two synchronized ContactManager spokes in action on Mac OS X. Click on thumbnail to view full-size image.

The client-side application UI (user interface) is written with AWT (Abstract Window Toolkit), the only standard UI library supported on PersonalJava or J2ME/FP/PP devices. Behind those UI drivers, we have another code layer that provides access to a generic on-device JDBC database. The database access layer also provides logics to synchronize mobile databases with backend databases through PointBase's proprietary UniSync synchronization server. Now, we focus on the code in the data access layer, which is contained in a single class: DBManager.

On-device data access

Class DBManager is a singleton class that provides a single point of entry to the database from the application. The Singleton pattern avoids threading complexities for embedded databases. The code snippet below shows DBManager's constructor and initialization method. It connects to the database, loads the table schema, populates the table with sample data, and creates SQL statement templates (PreparedStatement) for later use. As we can see, everything here is standard JDBC. For enterprise Java developers, the following code should be easy to understand:

Listing 1. Connect to mobile database and initialize access objects

class DBManager {
  // DBManager is a singleton class.
  private static DBManager instance;
  private String driver;
  private String url;
  private String user;
  private String password;
  private boolean delay;
  private Connection connection;
  private Statement statement;
  private PreparedStatement insert;
  private PreparedStatement find;
  private PreparedStatement delete;
  private PreparedStatement update;
  private PreparedStatement all;
  static DBManager getInstance() {
    if (instance == null) {
      instance = new DBManager();
    }
    return instance;
  }
  private DBManager() {
    // Get parameters from runtime properties.
    // This allows us to switch to different JDBC databases
    // without changing the application code.
    Properties properties = ContactManager.getProperties();
    driver =
      properties.getProperty("driver", "com.pointbase.me.jdbc.jdbcDriver");
    url =
      properties.getProperty("url", "jdbc:pointbase:micro:pbdemo");
    user =
      properties.getProperty("user", "PBPUBLIC");
    password =
      properties.getProperty("password", "PBPUBLIC");
    delay =
      properties.getProperty("delayread","true").equals("true");
    connect();
  }
  private void connect() {
    try {
      // Load the driver class.
      Class.forName(driver);
      // If the database doesn't exist, create a new database.
      connection = DriverManager.getConnection(url, user, password);
      // Create template statement objects.
      statement = connection.createStatement();
      createStatement();
      // If the database is newly created, load the schema.
      boolean newdb=initDatabase();
      // Load sample data for the new tables.
      if(newdb) {
         SampleDataCreator.insert(connection);
      }
    } catch (Exception e) {
      e.printStackTrace();
      System.exit(1);
    }
  }
  void disconnect() {
    try {
      connection.commit();
      statement.close();
      insert.close();
      find.close();
      delete.close();
      update.close();
      all.close();
      connection.close();
      System.exit(0);
    } catch (Exception e) {
      e.printStackTrace();
      System.exit(1);
    }
  }
  // Create the table and load the schema.
  private boolean initDatabase() {
    try {
      String sql = "CREATE TABLE NameCard (ID INT PRIMARY KEY, "+
        "Name VARCHAR(254), Company VARCHAR(254), Title VARCHAR(254), "+
        "Address1 VARCHAR(254), Address2 VARCHAR(254), "+
        "Phone VARCHAR(254), Email VARCHAR(254), "+
        "Picture Binary(1000000))";
      // If the table already exists, this will throw an exception.
      statement.executeUpdate(sql);
      // This means the database already exists.
      return true;
    } catch (SQLException e) {
      // Ignore the error - the table already exists, which is good
      // so we don't need to add demo data later on.
      return false;
    }
  }
  // Create statement templates.
  private void createStatement() {
    try {
      insert = connection.prepareStatement(
        "INSERT INTO NameCard (ID, Name, Company, Title, Address1, "+
        "Address2, Phone, Email, Picture) "+
        "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)");
      find = connection.prepareStatement(
        "SELECT * FROM NameCard WHERE (Name LIKE ?) "+
        "AND (Company LIKE ?) AND (Title LIKE ?) "+
        "AND ((Address1 LIKE ?) OR (Address2 LIKE ?)) "+
        "AND (Phone LIKE ?) AND (Email LIKE ?)");
      delete = connection.prepareStatement(
        "DELETE FROM NameCard WHERE ID = ?");
      update = connection.prepareStatement(
        "UPDATE NameCard SET ID=?, Name=?, Company=?, Title=?, "+
        "Address1=?, Address2=?, Phone=?, Email=?, Picture=? "+
        "WHERE ID = ?");
      all = connection.prepareStatement(
        "SELECT ID, Name, Company, Title, Address1, Address2, "+
        "Phone, Email FROM NameCard");
    } catch (SQLException e) {
      e.printStackTrace();
    }
  }
  
  // Other methods.
}

Other methods in DBManager provide access to the database via simple JDBC API calls. The following code snippet demonstrates methods for searching and manipulating name-card records. These methods heavily use the SQL templates we defined before.

Listing 2. Data access methods

1 2 3 Page
Recommended
Join the discussion
Be the first to comment on this article. Our Commenting Policies
See more