RecordStorepublic class RecordStore extends Object A class representing a record store. A record store consists of a
collection of records which will remain persistent across multiple
invocations of the MIDlet. The platform is responsible for
making its best effort to maintain the integrity of the
MIDlet's record stores throughout the normal use of the
platform, including reboots, battery changes, etc.
Record stores are created in platform-dependent locations, which
are not exposed to the MIDlets. The naming space for record stores
is controlled at the MIDlet suite granularity. MIDlets within a
MIDlet suite are allowed to create multiple record stores, as long
as they are each given different names. When a MIDlet suite is
removed from a platform all the record stores associated with its
MIDlets will also be removed. MIDlets within a MIDlet suite can
access each other's record stores directly. New APIs in MIDP
allow for the explicit sharing of record stores if the MIDlet
creating the RecordStore chooses to give such permission.
Sharing is accomplished through the ability to name a
RecordStore created by another MIDlet suite.
RecordStores are uniquely named using the unique name of the
MIDlet suite plus the name of the RecordStore. MIDlet suites are
identified by the MIDlet-Vendor and MIDlet-Name attributes from the
application descriptor.
Access controls are defined when RecordStores to be shared are
created. Access controls are enforced when RecordStores are
opened. The access modes allow private use or shareable
with any other MIDlet suite.
Record store names are case sensitive and may consist of any
combination of between one and 32 Unicode characters
inclusive. Record store names must be unique within the scope of a
given MIDlet suite. In other words, MIDlets within a MIDlet suite
are not allowed to create more than one record store with the same
name, however a MIDlet in one MIDlet suite is allowed to have a
record store with the same name as a MIDlet in another MIDlet
suite. In that case, the record stores are still distinct and
separate.
No locking operations are provided in this API. Record store
implementations ensure that all individual record store operations
are atomic, synchronous, and serialized, so no corruption will
occur with multiple accesses. However, if a MIDlet uses multiple
threads to access a record store, it is the MIDlet's responsibility
to coordinate this access or unintended consequences may result.
Similarly, if a platform performs transparent synchronization of a
record store, it is the platform's responsibility to enforce
exclusive access to the record store between the MIDlet and
synchronization engine.
Records are uniquely identified within a given record store by
their recordId, which is an integer value. This recordId is used as
the primary key for the records. The first record created in a
record store will have recordId equal to one (1). Each subsequent
record added to a RecordStore will be assigned a recordId one
greater than the record added before it. That is, if two records
are added to a record store, and the first has a recordId of 'n',
the next will have a recordId of 'n + 1'. MIDlets can create other
sequences of the records in the RecordStore by using the
RecordEnumeration class.
This record store uses long integers for time/date stamps, in
the format used by System.currentTimeMillis(). The record store is
time stamped with the last time it was modified. The record store
also maintains a version number, which is an integer that
is incremented for each operation that modifies the contents of the
RecordStore. These are useful for synchronization engines as well
as other things. |
Fields Summary |
---|
private static Vector | openRecordStorescache of open RecordStore instances | private com.sun.midp.rms.RecordStoreImpl | peerThe peer that performs the real functionallity. | private String | recordStoreNamename of this record store | private int | suiteIdunique id for suite that owns this record store | private int | opencountnumber of open instances of this record store | private Vector | recordListenerrecordListeners of this record store | private static com.sun.midp.security.SecurityToken | classSecurityTokenThe security token necessary to use RecordStoreImpl.
This is initialized in a static initialization block. | public static final int | AUTHMODE_PRIVATEAuthorization to allow access only to the current MIDlet
suite. AUTHMODE_PRIVATE has a value of 0. | public static final int | AUTHMODE_ANYAuthorization to allow access to any MIDlet
suites. AUTHMODE_ANY has a value of 1. |
Constructors Summary |
---|
private RecordStore(int suiteId, String recordStoreName)MIDlets must use openRecordStore() to get
a RecordStore object. If this constructor
is not declared (as private scope), Javadoc (and Java)
will assume a public constructor.
/*
* RecordStore Constructors
*/
this.suiteId = suiteId;
this.recordStoreName = recordStoreName;
recordListener = new java.util.Vector(3);
|
Methods Summary |
---|
public int | addRecord(byte[] data, int offset, int numBytes)Adds a new record to the record store. The recordId for this
new record is returned. This is a blocking atomic operation.
The record is written to persistent storage before the
method returns.
checkOpen();
checkWritable();
if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
Logging.report(Logging.INFORMATION, LogChannels.LC_RMS,
"addRecord(data=" + data +
", offset=" + offset +
", numBytes=" + numBytes + ")");
}
// validate parameters
if ((data == null) && (numBytes > 0)) {
throw new NullPointerException("illegal arguments: null " +
"data, numBytes > 0");
}
if ((offset < 0) || (numBytes < 0) ||
((data != null) && (offset + numBytes > data.length))) {
throw new ArrayIndexOutOfBoundsException();
}
int recordId = peer.addRecord(data, offset, numBytes);
if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
Logging.report(Logging.INFORMATION, LogChannels.LC_RMS,
"recordId = " + recordId);
}
// tell listeners a record has been added
notifyRecordAddedListeners(recordId);
return recordId;
| public void | addRecordListener(RecordListener listener)Adds the specified RecordListener. If the specified listener
is already registered, it will not be added a second time.
When a record store is closed, all listeners are removed.
synchronized (recordListener) {
if (!recordListener.contains(listener)) {
recordListener.addElement(listener);
}
}
| private void | checkOpen()Throws a RecordStoreNotOpenException if the RecordStore
is closed. (A RecordStore is closed if the RecordStoreFile
instance variable dbraf is null.
if (! isOpen()) {
throw new RecordStoreNotOpenException();
}
| private void | checkWritable()Internal method to determine if writing to this record store
is allowed for the calling MIDlet. Returns true
if isRecordStoreOwner() returns true or
dbAuthMode == 1 when isRecordStoreOwner()
returns false .
if (isRecordStoreOwner()) {
return;
} else {
if (peer.getAuthMode() == AUTHMODE_ANY) { // Read-Write mode
return;
}
}
throw new SecurityException("no write access");
| public void | closeRecordStore()This method is called when the MIDlet requests to have the
record store closed. Note that the record store will not
actually be closed until closeRecordStore() is called as many
times as openRecordStore() was called. In other words, the
MIDlet needs to make a balanced number of close calls as open
calls before the record store is closed.
When the record store is closed, all listeners are removed
and all RecordEnumerations associated with it become invalid.
If the MIDlet attempts to perform
operations on the RecordStore object after it has been closed,
the methods will throw a RecordStoreNotOpenException.
checkOpen();
synchronized (openRecordStores) {
if (--opencount <= 0) { // free stuff - final close
openRecordStores.removeElement(this);
peer.closeRecordStore();
// mark this RecordStore as closed
peer = null;
}
}
| public void | deleteRecord(int recordId)The record is deleted from the record store. The recordId for
this record is NOT reused.
if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
Logging.report(Logging.INFORMATION, LogChannels.LC_RMS,
"deleteRecord(" + recordId + ")");
}
checkOpen();
checkWritable();
peer.deleteRecord(recordId);
// tell listeners a record has been deleted
notifyRecordDeletedListeners(recordId);
| public static void | deleteRecordStore(java.lang.String recordStoreName)Deletes the named record store. MIDlet suites are only allowed
to delete their own record stores. If the named record store is
open (by a MIDlet in this suite or a MIDlet in a different
MIDlet suite) when this method is called, a
RecordStoreException will be thrown. If the named record store
does not exist a RecordStoreNotFoundException will be
thrown. Calling this method does NOT result in recordDeleted
calls to any registered listeners of this RecordStore.
int id = MIDletStateHandler.getMidletStateHandler().
getMIDletSuite().getID();
if (recordStoreName == null || recordStoreName.length() == 0) {
throw new RecordStoreNotFoundException();
}
// Check the record store cache for a db with the same name
synchronized (openRecordStores) {
RecordStore db;
int size = openRecordStores.size();
for (int n = 0; n < size; n++) {
db = (RecordStore)openRecordStores.elementAt(n);
if (db.suiteId == id &&
db.recordStoreName.equals(recordStoreName)) {
// cannot delete an open record store
throw new RecordStoreException("deleteRecordStore error:"
+ " record store is"
+ " still open");
}
}
// this record store is not currently open
RecordStoreImpl.deleteRecordStore(
classSecurityToken, id, recordStoreName);
}
| private static javax.microedition.rms.RecordStore | doOpen(int suiteId, java.lang.String recordStoreName, boolean createIfNecessary)Internal method to open (and possibly create) a record store associated
with the given MIDlet suite. If this method is called by a MIDlet when
the record store is already open by a MIDlet in the MIDlet suite,
this method returns a reference to the same RecordStoreImpl object.
RecordStore recordStore;
if (recordStoreName.length() > 32 || recordStoreName.length() == 0) {
throw new IllegalArgumentException();
}
synchronized (openRecordStores) {
// Save record store instances and ensure that there is only
// one record store object in memory for any given record
// store file. This is good for memory use. This is NOT safe
// in the situation where multiple VM's may be executing code
// concurrently. In that case, you have to sync things through
// file locking or something similar.
// Check the record store instance list for a db with the same name
int size = openRecordStores.size();
for (int n = 0; n < size; n++) {
recordStore = (RecordStore)openRecordStores.elementAt(n);
if (recordStore.suiteId == suiteId &&
recordStore.recordStoreName.equals(recordStoreName)) {
recordStore.opencount++; // increment the open count
return recordStore; // return ref to cached record store
}
}
/*
* Record store not found in cache, so create it.
* If createIfNecessary is FALSE and the RecordStore
* does not exists, a RecordStoreNotFoundException is
* thrown.
*/
recordStore = new RecordStore(suiteId, recordStoreName);
recordStore.peer = RecordStoreImpl.openRecordStore(
classSecurityToken, suiteId, recordStoreName,
createIfNecessary);
/*
* Now add the new record store to the cache
*/
recordStore.opencount = 1;
openRecordStores.addElement(recordStore);
}
return recordStore;
| public RecordEnumeration | enumerateRecords(RecordFilter filter, RecordComparator comparator, boolean keepUpdated)Returns an enumeration for traversing a set of records in the
record store in an optionally specified order.
The filter, if non-null, will be used to determine what
subset of the record store records will be used.
The comparator, if non-null, will be used to determine the
order in which the records are returned.
If both the filter and comparator is null, the enumeration
will traverse all records in the record store in an undefined
order. This is the most efficient way to traverse all of the
records in a record store. If a filter is used with a null
comparator, the enumeration will traverse the filtered records
in an undefined order.
The first call to RecordEnumeration.nextRecord()
returns the record data from the first record in the sequence.
Subsequent calls to RecordEnumeration.nextRecord()
return the next consecutive record's data. To return the record
data from the previous consecutive from any
given point in the enumeration, call previousRecord() .
On the other hand, if after creation the first call is to
previousRecord() , the record data of the last element
of the enumeration will be returned. Each subsequent call to
previousRecord() will step backwards through the
sequence.
checkOpen();
return new RecordEnumerationImpl(this, filter,
comparator, keepUpdated);
| public long | getLastModified()Returns the last time the record store was modified, in the
format used by System.currentTimeMillis().
checkOpen();
return peer.getLastModified();
| public java.lang.String | getName()Returns the name of this RecordStore.
checkOpen();
return recordStoreName;
| public int | getNextRecordID()Returns the recordId of the next record to be added to the
record store. This can be useful for setting up pseudo-relational
relationships. That is, if you have two or more
record stores whose records need to refer to one another, you can
predetermine the recordIds of the records that will be created
in one record store, before populating the fields and allocating
the record in another record store. Note that the recordId returned
is only valid while the record store remains open and until a call
to addRecord() .
checkOpen();
return peer.getNextRecordID();
| public int | getNumRecords()Returns the number of records currently in the record store.
checkOpen();
return peer.getNumRecords();
| public int | getRecord(int recordId, byte[] buffer, int offset)Returns the data stored in the given record.
checkOpen();
return peer.getRecord(recordId, buffer, offset);
| public byte[] | getRecord(int recordId)Returns a copy of the data stored in the given record.
checkOpen();
return peer.getRecord(recordId);
| int[] | getRecordIDs()Returns all of the recordId's currently in the record store.
return peer.getRecordIDs();
| public int | getRecordSize(int recordId)Returns the size (in bytes) of the MIDlet data available
in the given record.
checkOpen();
int size = peer.getRecordSize(recordId);
if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
Logging.report(Logging.INFORMATION, LogChannels.LC_RMS,
"getSize(" + recordId + ") = " + size);
}
return size;
| public int | getSize()Returns the amount of space, in bytes, that the record store
occupies. The size returned includes any overhead associated
with the implementation, such as the data structures
used to hold the state of the record store, etc.
checkOpen();
return peer.getSize();
| public int | getSizeAvailable()Returns the amount of additional room (in bytes) available for
this record store to grow. Note that this is not necessarily
the amount of extra MIDlet-level data which can be stored,
as implementations may store additional data structures with
each record to support integration with native applications,
synchronization, etc.
checkOpen();
int sizeAvailable = peer.getSizeAvailable();
if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
Logging.report(Logging.INFORMATION, LogChannels.LC_RMS,
"getSizeAvailable() = " + sizeAvailable);
}
return sizeAvailable;
| public int | getVersion()Each time a record store is modified (by
addRecord , setRecord , or
deleteRecord methods) its version is
incremented. This can be used by MIDlets to quickly tell if
anything has been modified.
The initial version number is implementation dependent.
The increment is a positive integer greater than 0.
The version number increases only when the RecordStore is updated.
The increment value need not be constant and may vary with each
update.
checkOpen();
return peer.getVersion();
| boolean | isOpen()Get the open status of this record store. (Package accessible
for use by record enumeration objects.)
synchronized (openRecordStores) {
return (peer != null);
}
| private boolean | isRecordStoreOwner()Internal method to check record store owner vs. the vendor and suite
of the currently running midlet
int currentId = MIDletStateHandler.getMidletStateHandler().
getMIDletSuite().getID();
return (suiteId == currentId);
| public static java.lang.String[] | listRecordStores()Returns an array of the names of record stores owned by the
MIDlet suite. Note that if the MIDlet suite does not
have any record stores, this function will return null.
The order of RecordStore names returned is implementation
dependent.
MIDletSuite currentSuite =
MIDletStateHandler.getMidletStateHandler().getMIDletSuite();
if (currentSuite == null) {
return null;
}
// static calls synchronize on openRecordStores
synchronized (openRecordStores) {
return RecordStoreImpl.listRecordStores(
classSecurityToken, currentSuite.getID());
}
| private void | notifyRecordAddedListeners(int recordId)Notifies all registered listeners that a record was added.
synchronized (recordListener) {
if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
Logging.report(Logging.INFORMATION, LogChannels.LC_RMS,
"notify Add # listener = " +
recordListener.size());
}
int numListeners = recordListener.size();
for (int i = 0; i < numListeners; i++) {
RecordListener rl =
(RecordListener)recordListener.elementAt(i);
rl.recordAdded(this, recordId);
}
}
| private void | notifyRecordChangedListeners(int recordId)Notifies all registered listeners that a record changed.
synchronized (recordListener) {
if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
Logging.report(Logging.INFORMATION, LogChannels.LC_RMS,
"notify Change # listener = " +
recordListener.size());
}
int numListeners = recordListener.size();
for (int i = 0; i < numListeners; i++) {
RecordListener rl = (RecordListener)recordListener.elementAt(i);
rl.recordChanged(this, recordId);
}
}
| private void | notifyRecordDeletedListeners(int recordId)Notifies all registered listeners that a record was deleted.
synchronized (recordListener) {
if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
Logging.report(Logging.INFORMATION, LogChannels.LC_RMS,
"notify Delete # listener = " +
recordListener.size());
}
int numListeners = recordListener.size();
for (int i = 0; i < numListeners; i++) {
RecordListener rl = (RecordListener)recordListener.elementAt(i);
rl.recordDeleted(this, recordId);
}
}
| static javax.microedition.rms.RecordStore | openForLockTesting(java.lang.String recordStoreName)Internal method to open a record store for lock testing. When
lock testing the record store will be obtained from the cache or
put in the cache, this allows testing of lower level locking without
the need to run multiple Isolates.
RecordStore recordStore;
int suiteId = MIDletStateHandler.getMidletStateHandler().
getMIDletSuite().getID();
recordStore = new RecordStore(suiteId, recordStoreName);
recordStore.peer = RecordStoreImpl.openRecordStore(
classSecurityToken, suiteId, recordStoreName,
false);
recordStore.opencount = 1;
return recordStore;
| public static javax.microedition.rms.RecordStore | openRecordStore(java.lang.String recordStoreName, boolean createIfNecessary)Open (and possibly create) a record store associated with the
given MIDlet suite. If this method is called by a MIDlet when
the record store is already open by a MIDlet in the MIDlet suite,
this method returns a reference to the same RecordStore object.
int id = MIDletStateHandler.getMidletStateHandler().
getMIDletSuite().getID();
return doOpen(id, recordStoreName, createIfNecessary);
| public static javax.microedition.rms.RecordStore | openRecordStore(java.lang.String recordStoreName, boolean createIfNecessary, int authmode, boolean writable)Open (and possibly create) a record store that can be shared
with other MIDlet suites. The RecordStore is owned by the
current MIDlet suite. The authorization mode is set when the
record store is created, as follows:
AUTHMODE_PRIVATE - Only allows the MIDlet
suite that created the RecordStore to access it. This
case behaves identically to
openRecordStore(recordStoreName,
createIfNecessary) .
AUTHMODE_ANY - Allows any MIDlet to access the
RecordStore. Note that this makes your recordStore
accessible by any other MIDlet on the device. This
could have privacy and security issues depending on
the data being shared. Please use carefully.
The owning MIDlet suite may always access the RecordStore and
always has access to write and update the store.
If this method is called by a MIDlet when the record store
is already open by a MIDlet in the MIDlet suite, this method
returns a reference to the same RecordStore object.
RecordStore recordStore;
boolean isExistingStorage = false;
/*
* First, we have to check if the record store already exists or not.
* If we open an existing record store, "authmode" must be ignored!
*
*/
try {
recordStore = openRecordStore(recordStoreName, false);
isExistingStorage = true;
} catch (RecordStoreNotFoundException ex)
{
recordStore = openRecordStore(recordStoreName, createIfNecessary);
}
if (!isExistingStorage) {
try {
recordStore.peer.setMode(authmode, writable);
} catch (Exception e) {
try {
recordStore.closeRecordStore();
} catch (Exception ex) {
// do not overthrow the real exception
}
try {
int id = MIDletStateHandler.getMidletStateHandler().
getMIDletSuite().getID();
RecordStoreImpl.deleteRecordStore(
classSecurityToken, id, recordStoreName);
} catch (Exception ex) {
// do not overthrow the real exception
}
if (e instanceof RecordStoreException) {
throw (RecordStoreException)e;
}
throw (RuntimeException)e;
}
}
return recordStore;
| public static javax.microedition.rms.RecordStore | openRecordStore(java.lang.String recordStoreName, java.lang.String vendorName, java.lang.String suiteName)Open a record store associated with the named MIDlet suite.
The MIDlet suite is identified by MIDlet vendor and MIDlet
name. Access is granted only if the authorization mode of the
RecordStore allows access by the current MIDlet suite. Access
is limited by the authorization mode set when the record store
was created:
AUTHMODE_PRIVATE - Succeeds only if vendorName
and suiteName identify the current MIDlet suite; this
case behaves identically to
openRecordStore(recordStoreName,
createIfNecessary) .
AUTHMODE_ANY - Always succeeds.
Note that this makes your recordStore
accessible by any other MIDlet on the device. This
could have privacy and security issues depending on
the data being shared. Please use carefully.
Untrusted MIDlet suites are allowed to share data but
this is not recommended. The authenticity of the
origin of untrusted MIDlet suites cannot be verified
so shared data may be used unscrupulously.
If this method is called by a MIDlet when the record store
is already open by a MIDlet in the MIDlet suite, this method
returns a reference to the same RecordStore object.
If a MIDlet calls this method to open a record store from
its own suite, the behavior is identical to calling:
{@link #openRecordStore(String, boolean)
openRecordStore(recordStoreName, false)}
int currentID = MIDletStateHandler.getMidletStateHandler().
getMIDletSuite().getID();
int id;
RecordStore recordStore;
if (vendorName == null || suiteName == null) {
throw new IllegalArgumentException("vendorName and " +
"suiteName must be " +
"non null");
}
if (recordStoreName.length() > 32 || recordStoreName.length() == 0) {
throw new IllegalArgumentException();
}
id = MIDletSuiteStorage.getSuiteID(vendorName, suiteName);
if (id == MIDletSuite.UNUSED_SUITE_ID) {
throw new RecordStoreNotFoundException();
}
recordStore = doOpen(id, recordStoreName, false);
if ((currentID != id) &&
(recordStore.peer.getAuthMode() == AUTHMODE_PRIVATE)) {
recordStore.closeRecordStore();
throw new SecurityException();
}
return recordStore;
| public void | removeRecordListener(RecordListener listener)Removes the specified RecordListener. If the specified listener
is not registered, this method does nothing.
synchronized (recordListener) {
recordListener.removeElement(listener);
}
| public void | setMode(int authmode, boolean writable)Changes the access mode for this RecordStore. The authorization
mode choices are:
AUTHMODE_PRIVATE - Only allows the MIDlet
suite that created the RecordStore to access it. This
case behaves identically to
openRecordStore(recordStoreName,
createIfNecessary) .
AUTHMODE_ANY - Allows any MIDlet to access the
RecordStore. Note that this makes your recordStore
accessible by any other MIDlet on the device. This
could have privacy and security issues depending on
the data being shared. Please use carefully.
The owning MIDlet suite may always access the RecordStore and
always has access to write and update the store. Only the
owning MIDlet suite can change the mode of a RecordStore.
checkOpen();
if (! isRecordStoreOwner()) {
throw new SecurityException("not the owner");
} else if (authmode != AUTHMODE_PRIVATE &&
authmode != AUTHMODE_ANY) {
throw new IllegalArgumentException();
}
peer.setMode(authmode, writable);
| public void | setRecord(int recordId, byte[] newData, int offset, int numBytes)Sets the data in the given record to that passed in. After
this method returns, a call to getRecord(int recordId)
will return an array of numBytes size containing the data
supplied here.
if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
Logging.report(Logging.INFORMATION, LogChannels.LC_RMS,
"setRecord("+recordId+")");
}
// validate parameters
if ((newData == null) && (numBytes > 0)) {
throw new NullPointerException("illegal arguments: null " +
"data, numBytes > 0");
}
if ((offset < 0) || (numBytes < 0) ||
((newData != null) && (offset + numBytes > newData.length))) {
throw new ArrayIndexOutOfBoundsException();
}
checkOpen();
checkWritable();
peer.setRecord(recordId, newData, offset, numBytes);
// update database header info and sync to file
notifyRecordChangedListeners(recordId);
|
|