You are on page 1of 12

J.E.D.I.

The Record Management System


MIDP provides an API where programs can persist application data locally on the device.
The MIDP Record Management System is a facility for MIDlets to store application data
across MIDlet invocations. Data is stored into the non-volatile memory of the device.
This means that stored program data will not be lost even if the program is restarted or
the device is turned off.

The Record Management System is a record-oriented database system for MIDP devices.
A record in this database is simply an array of bytes with an index.

.1 Objectives

At the end of the lesson, the student should be able to:

• Understand the concept of a Record Store

• Create and Open Record Stores

• Add, Retrieve, Update and Delete records

• Enumerate records using a RecordEnumerator

• Create a Record Comparator

• Create a Record Filter

.2 The Record Store

A Record Store is a collection of records. Record Ids in a Record Store are unique.
Record Ids are automatically allocated at record creation time and act as the index or
primary key. Record Ids are assigned sequentially, and the first Record ID assigned to
the first record of every Record Store is 1 (one).

When a record is deleted, its record id will not be reused. If we create four records and
delete the fourth record, the next record Id that the system will assign is 5 (see Figure).

Record ID Array of bytes

1 Data of record #1

2 Data of record #2

Mobile Application Development 1


J.E.D.I.

Record ID Array of bytes

3 Data of record #3

5 Data of record #5

MIDlets can create more than one record store. The record store name should be unique
within the MIDlet suite. Record store names are case sensitive. The maximum length of
a Record Store is 32 characters.

When a MIDlet suite is deleted from a device, all record stores associated with the
MIDlets in this suite will also be deleted.

Mobile Application Development 2


J.E.D.I.

Creating and Opening Record Stores

The following method creates or opens Record Stores:

static RecordStore openRecordStore(String recordStoreName,


boolean createIfNecessary)

static RecordStore openRecordStore(String recordStoreName,


boolean createIfNecessary, int authmode,
boolean writable)

static RecordStore openRecordStore(String recordStoreName,


String vendorName, String suiteName)

../../../Saave_Lahi/javax/microedition/rms/RecordStore.html
If createIfNecessary is set to true and the Record Store does not yet exist, the Record
Store will be created. If createIfNecessary is set to false and the Record Store is non-
existent, a RecordStoreNotFoundException will be thrown.

The authmode parameter may be RecordStore.AUTHMODE_PRIVATE or


RecordStore.AUTHMODE_ANY. Using AUTHMODE_PRIVATE will cause the Record Store to
be accessible only by the MIDlet suite of the owner MIDlet. Setting authmode to
AUTHMODE_ANY will make this Record Store to be accessible by ANY MIDlet. The access
mode is specified by the boolean parameter writable. To allow other MIDlets (outside of
the MIDlet suite) to write on this record store, this parameter must be set to true.

Using the first form of the openRecordStore() method will cause the Record store to be
accessible only by MIDlets in the same suite (authmode set to AUTHMODE_PRIVATE).

To open an existing Record Store from a different MIDlet suite, the third form of the
openRecordStore method should be used. The vendorName and the MIDlet suiteName
must be specified.

If the Record Store is already open, these methods will return the reference to this
Record Store. The system keeps track of how many times Record Stores have been
opened. Each Record Store must be closed the same number of times it has been
opened.

Mobile Application Development 3


J.E.D.I.

Adding a Record

int addRecord(byte[] data, int offset, int numBytes)

The method addRecord will create a new Record on the Record Store and return the
Record ID.

Retrieving Records

byte[] getRecord(int recordId)

int getRecord(int recordId, byte[] buffer, int offset)

int getRecordSize(int recordId)

The first form of the getRecord method returns a copy of the data stored on the record
given the Record ID. The second form will copy the data on the supplied byte array
parameter. When using the second form, the byte array must have been previously
allocated. If the size of the record is larger than the size of the parameter, an
ArrayIndexOutOfBoundsException will be thrown. You can use the method getRecordSize
in order to know beforehand the size of the record you are going to read.

Mobile Application Development 4


J.E.D.I.

Updating a Record

We cannot modify only part of the record data. If we want to modify a record, we have
to:
1. read the record using getRecord
2. update the record in memory
3. call setRecord to write the updated record data.

void setRecord(int recordId, byte[] newData, int offset,


int numBytes)

Deleting a Record

void deleteRecord(int recordId)

When a record is deleted, the record Id will not be reused in the next calls to addRecord.
This means that there could be a gap in the Record Ids. So it is not advisable to use an
incrementing counter to list all the records in a record store. The Record Enumerator is
what we should use to go through the list of records in a store.

Closing a Record Store

void closeRecordStore()

Calling closeRecordStore() will not close the Record Store until it has been called the
same number of times openRecordStore() has been called on a particular store. The
system counts how many times a record store is opened. Calling closeRecordStore()
more times than it has been opened will cause a RecordStoreNotOpen Exception.

Mobile Application Development 5


J.E.D.I.

This snippet from the example RmsExample1 is a simple MIDlet that demontrates how to
create a Record Store, add records and retrieve all the records from the Record Store:

// Open or Create Record Store with name "RmsExample1"


recStore = RecordStore.openRecordStore("RmsExample1", true);

// Load contents of Record Store


for (int recId=1; recId<=recStore.getNumRecords(); recId++){
// getRecord returns the length of the record
recLength = recStore.getRecord(recId, data, 0);

// Convert the byte array into String


String item = new String(data, 0, recLength);
...
}

...
// This is the String we will put into the Record
String newItem = "Record #" + recStore.getNextRecordID();

// Convert the String into a byte array


byte[] bytes = newItem.getBytes();

// Write the Record into the Store


recStore.addRecord(bytes, 0, bytes.length);

Programming Tips:

1. The starting Record ID is 1, and not 0. When using loops, remember to use 1 as the
starting index and not 0.
2. It is better to use a Record Enumerator than using an incrementing index (as in this
example). Deleted records will still be read by this example, which will cause an
InvalidRecordIDException Exception.

Mobile Application Development 6


J.E.D.I.

Obtaining the List of Record Stores in a MIDlet Suite

static String[] listRecordStores()

This method returns an array of the names of record stores owned by the MIDlet suite. If
the MIDlet suite does not have a Record Store, this method will return null.

String[] storeNames = RecordStore.listRecordStores();


System.out.println("Record Stores for this MIDlet suite:");

for (int i=0; storeNames != null && i<storeNames.length; i++){


System.out.println(storeNames[i]);
}

Example: RmsListStores

Record Stores for this MIDlet suite:


Prefs
RmsExample1
RmsExample2

Sample Output of RmsListStores

The order of the names returned is not defined and is implementation dependent. So if
we want to display the names ordered alphabetically, we have to sort the array first.

Mobile Application Development 7


J.E.D.I.

Storing Java Primitives

So far, the data that we have been writing and reading from the record store are
Strings. CLDC includes the standard classes for primitive manipulation. These classes
are from the standard library of Java 2 Platform, Standard Edition (J2SE).

We can write Java primitives by combining the ByteArrayOutputStream and the


DataOutputStream classes. Reading primitives (int, long, short, string, boolean, etc) is
also done using the ByteArrayInputStream and DataInputStream.

ByteArrayOutputStream out = new ByteArrayOutputStream();


DataOutputStream dOut = new DataOutputStream(out);

// Store an integer (square of the record id


dOut.writeInt(recStore.getNextRecordID() * recStore.getNextRecordID());

// Store a string
dOut.writeUTF("Record #" + recStore.getNextRecordID());

byte[] bytes = out.toByteArray();

// Write the Record into the Store


recStore.addRecord(bytes, 0, bytes.length);
...

// Get the next record


byte[] recBytes = enumerator.nextRecord();

ByteArrayInputStream in = new ByteArrayInputStream(recBytes);


DataInputStream dIn = new DataInputStream(in);
int count = dIn.readInt();
String item = dIn.readUTF();

Mobile Application Development 8


J.E.D.I.

Other methods for Record Stores

long getLastModified()

int getVersion()

The system remembers when a record store was last modified. The method
getLastModified returns the last time a record store was last modified. It returns a long
value, in the format used by System.currentTimeMillis().

All record stores maintain a version information. Every time a record is modified, their
version number is updated. Using the methods addRecord, setRecord and deleteRecord
will cause the version number of record store to be incremented.

static void deleteRecordStore(String recordStoreName)


Deletes the named record store.

String getName()
Returns the name of this RecordStore.

int getNextRecordID()
Returns the recordId of the next record to be added to the
record store.

int getNumRecords()
Returns the number of records currently in the record
store.

int getSize()
Returns the amount of space, in bytes, that the record
store occupies.

int getSizeAvailable()
Returns the amount of additional room (in bytes) available
for this record store to grow.

void setMode(int authmode, boolean writable)


Changes the access mode for this RecordStore.

.3 Record Enumeration

Traversing a Record Store using an incrementing index is inefficient. Deleted record


stores will have to be skipped over since Record Ids of deleted records are not reused.

Using a record enumerator solves the problem of having to look out for deleted records.
We can also sort the order of the enumeration by passing a comparator method. By
passing a filter method, we can also skip records of no interest to our output.

Mobile Application Development 9


J.E.D.I.

RecordEnumeration enumerateRecords(RecordFilter filter,


RecordComparator comparator, boolean keepUpdated)

The enumerateRecords method of a record store will return an enumeration for


traversing all the records in a store. This is the recommended way to traverse all the
records in a record store. We will be discussing the filter and comparator in the next
lessons.

The simplest way to use this method is to use null for both filter and comparator. This
will return an enumeration off all the records in a store in an undefined order.

// Open or Create Record Store with name "RmsExample2"


recStore = RecordStore.openRecordStore("RmsExample2", true);

// Load contents of Record Store


RecordEnumeration enumerator
= recStore.enumerateRecords(null, null, false);

while (enumerator.hasNextElement()){
// Get the next record and convert the byte array into String
String item = new String(enumerator.nextRecord());

// Do something with the record


...
}

.4 Record Comparator

The ordering of an enumeration can be defined using a Record Comparator. A Record


Comparator is passed on the method enumerateRecords. If we want to sort the output of
an enumeration, we have to define a comparator and pass that method as a second
parameter to enumerateRecords.

int compare(byte[] rec1, byte[] rec2)

In order to create a Record Comparator, we must implement the RecordComparator


interface. This interface defines a single method, compare(byte[] rec1, byte[] rec2). This
method must return, RecordComparator.FOLLOWS or RecordComparator.PRECEDES if
rec1 follows or precedes rec2 in the sort order, respectively.
RecordComparator.EQUIVALENT should be returned if rec1 is equivalent to rec2 in the
sort order.

// Create an enumeration, sorted alphabetically


RecordEnumeration enumerator
= recStore.enumerateRecords(null, new AlphaOrder(), false);
...

// Sort alphabetically
class AlphaOrder implements RecordComparator {

Mobile Application Development 10


J.E.D.I.

public int compare(byte[] rec1, byte[] rec2){


String record1 = new String(rec1).toUpperCase();
String record2 = new String(rec2).toUpperCase();

if (record1.compareTo(record2) < 0){


return(PRECEDES);
} else {
if (record1.compareTo(record2) > 0){
return(FOLLOWS);
} else {
return(EQUIVALENT);
}
}
}
}

.5 Record Filter

The examples we have seen so far reads all the records from the store. We can use a
filter to get only the records that we want.

Our program must implement the match() method to select our target record(s). It
should return true if the record matches our criteria. It should return false otherwise.

boolean matches(byte[] candidate)

public boolean matches(byte[] candidate){


boolean isaMatch = false;
try {
ByteArrayInputStream bin = new ByteArrayInputStream(candidate);
DataInputStream dIn = new DataInputStream(bin);

int count = dIn.readInt();


String item = dIn.readUTF();

// return only records with contents ending in 0


if (item.endsWith("0")){
isaMatch = true;
} else {
isaMatch = false;

Mobile Application Development 11


J.E.D.I.

}
} catch (Exception e){items.append(e.toString(), null); }
return(isaMatch);
}

.6 Exercises

.6.1 Preferences Store

Create a class that can persist program preferences. The class will store the preferences
into a Record Store. Each record will have a variable name and value. Each
variable/value pair is stored in a single record. The name and value are stored in the
database as Strings.

The class should implement these methods:

public String readVar(RecordStore recStore, String name, String defaultValue){


public void writeString(RecordStore recStore, String name, String value);

Mobile Application Development 12