FileDocCategorySizeDatePackage
RecordStoreImpl.javaAPI DocphoneME MR2 API (J2ME)44836Wed May 02 18:00:12 BST 2007com.sun.midp.rms

RecordStoreImpl

public class RecordStoreImpl extends Object implements AbstractRecordStoreImpl
A class implementing a MIDP a record store.

Fields Summary
private byte[]
compactBuffer
used to compact the records of the record store
static final int
AUTHMODE_ANY_RO
Internal indicator for AUTHMODE_ANY with read only access AUTHMODE_ANY_RO has a value of 2.
int
suiteId
unique id for suite that owns this record store
Object
rsLock
lock used to synchronize this record store
byte[]
dbHeader
data block header stored here
private RecordStoreIndex
dbIndex
record store index
private RecordStoreFile
dbFile
record store data
Constructors Summary
private RecordStoreImpl(int suiteId, String recordStoreName, boolean create)
Creates a RecordStoreImpl instance; for internal use only. Callers from outside must use openRecordStore().

param
suiteId unique ID of the suite that owns the store
param
recordStoreName a string to name the record store
param
create if true, create the record store if it doesn't exist
exception
RecordStoreException if something goes wrong setting up the new RecordStore.
exception
RecordStoreNotFoundException if can't find the record store and create is set to false.
exception
RecordStoreFullException if there is no room in storage to create a new record store


        this.suiteId = suiteId;
        rsLock = new Object();

        boolean exists = RecordStoreUtil.exists(suiteId, recordStoreName,
                                                RecordStoreFile.DB_EXTENSION);

        // Check for errors between app and record store existance.
        if (!create && !exists) {
            throw new RecordStoreNotFoundException("cannot find record "
                                                   + "store file");
        }

        /*
         * If a new RecordStoreImpl will be created in storage,
         * check to see if the space required is available.
         */
        if (create && !exists) {
            int space = RecordStoreFile.spaceAvailableNewRecordStore(suiteId);
            if (space - DB_HEADER_SIZE < 0) {
                throw new RecordStoreFullException();
            }
        }

        // Create a RecordStoreFile for storing the record store.
        try {
            dbFile = new RecordStoreFile(suiteId, recordStoreName,
                                         RecordStoreFile.DB_EXTENSION);

            // allocate a new header
            dbHeader = new byte[DB_HEADER_SIZE];

            if (exists) {
                // load header
                dbFile.read(dbHeader);

                /*
                 * Verify that the file is actually a record store
                 * by verifying the record store "signature."
                 */
                for (int i = 0; i < DB_SIGNATURE.length; i++) {
                    if (dbHeader[i] != DB_SIGNATURE[i]) {
                        throw new RecordStoreException("invalid record "+
                                                       "store contents");
                    }
                }
            } else {
                // initialize the header
                for (int i = 0; i < DB_SIGNATURE.length; i++) {
                    dbHeader[i] = DB_SIGNATURE[i];
                }

                RecordStoreUtil.putInt(1, dbHeader, RS2_NEXT_ID);
                RecordStoreUtil.putLong(System.currentTimeMillis(), dbHeader,
                        RS5_LAST_MODIFIED);

                // write the header to the file
                dbFile.write(dbHeader);
                dbFile.commitWrite();
            }

            // create the index object
            dbIndex = new RecordStoreIndex(this, suiteId, recordStoreName);

        } catch (java.io.IOException ioe) {
            try {
                if (dbFile != null) {
                    dbFile.close();
                }
            } catch (java.io.IOException ioe2) {
                // ignore exception within exception block
            }

            if (!exists) {
                // avoid preserving just created damaged files
                RecordStoreUtil.quietDeleteFile(suiteId,
                    recordStoreName, RecordStoreFile.DB_EXTENSION);
                RecordStoreIndex.deleteIndex(suiteId, recordStoreName);
            }

            dbFile = null;
            throw new RecordStoreException("error opening record store " +
                                           "file");
        }
    
Methods Summary
private intaddBlock(int recordId, byte[] data, int offset, int numBytes)
Adds a block for the record and data or sets an existing block to the data. Splits an exiting block if needed.

param
recordId the ID of the record to use in this operation
param
data the new data to store in the record
param
offset the index into the data buffer of the first relevant byte for this record
param
numBytes the number of bytes of the data buffer to use for this record
exception
RecordStoreFullException if the operation cannot be completed because the record store has no more room
exception
IOException if there is an error accessing the db file
exception
RecordStoreException if the new consumption is gong to exceed the resource limit
return
the offset in the db file of the block added


        if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
            Logging.report(Logging.INFORMATION, LogChannels.LC_RMS,
                           "addBlock("+recordId+") numBytes="+numBytes);
        }

        // calculate the size of the block needed
        int blockSize = RecordStoreUtil.calculateBlockSize(numBytes);
        int blockOffset = 0;
        int freeBlocksSize = RecordStoreUtil.getInt(dbHeader, RS7_FREE_SIZE);

        // initialize the block header
        byte[] header = new byte[BLOCK_HEADER_SIZE];

        // check if there is the potential for a large enough free block
        if (freeBlocksSize >= blockSize) {

            if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
                Logging.report(Logging.INFORMATION, LogChannels.LC_RMS,
                               "blockSize = " + blockSize +
                               "free size = " + freeBlocksSize);
            }

            // initialize the number of bytes and search for an
            // available free block
            RecordStoreUtil.putInt(numBytes, header, 4);
            blockOffset = dbIndex.getFreeBlock(header);
        }

        // set the recordId in to the block header
        RecordStoreUtil.putInt(recordId, header, 0);

        if (blockOffset > 0) {
            // search found a block, use it
            splitBlock(blockOffset, header, data, offset, numBytes);
        } else {
            // search failed, add a new block to the end of the db file
            int spaceAvailable = getSizeAvailable();
            /**
             * spaceAvailable returns the smaller number of total space
             * available and the storage limit per suite. If it is less than
             * the block size to be added, RecordStoreFullException is thrown
             */
            if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
                Logging.report(Logging.INFORMATION, LogChannels.LC_RMS,
                               "spaceAvailable = "+spaceAvailable);
            }

            // Is there room to grow the file?
            if (spaceAvailable < blockSize) {
                 // Is there enough room totally: in storage and free blocks?
                 if (spaceAvailable + freeBlocksSize < blockSize) {
                     throw new RecordStoreFullException();
                 }
                 compactRecords();
            }

            blockOffset = getSize();

            // seek to the location and write the block and header
            RecordStoreUtil.putInt(numBytes, header, 4);
            writeBlock(blockOffset, header, data, offset, numBytes);

            // update the db data size
            RecordStoreUtil.putInt(RecordStoreUtil.getInt(dbHeader, RS6_DATA_SIZE) + blockSize,
                   dbHeader, RS6_DATA_SIZE);
            dbFile.seek(RS6_DATA_SIZE);
            dbFile.write(dbHeader, RS6_DATA_SIZE, 4);
            // dbFile.commitWrite();

            if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
                Logging.report(Logging.INFORMATION, LogChannels.LC_RMS,
                               "blockOffset = "+blockOffset);
            }
        }

        return blockOffset;
    
public intaddRecord(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.

param
data the data to be stored in this record. If the record is to have zero-length data (no data), this parameter may be null.
param
offset the index into the data buffer of the first relevant byte for this record
param
numBytes the number of bytes of the data buffer to use for this record (may be zero)
return
the recordId for the new record
exception
RecordStoreNotOpenException if the record store is not open
exception
RecordStoreException if a different record store-related exception occurred
exception
RecordStoreFullException if the operation cannot be completed because the record store has no more room
exception
SecurityException if the MIDlet has read-only access to the RecordStore


        synchronized (rsLock) {
            int recordId = getNextRecordID();

            try {
                // add a block for this record
                addBlock(recordId, data, offset, numBytes);

                // update the db header
                RecordStoreUtil.putInt(recordId+1, dbHeader, RS2_NEXT_ID);
                RecordStoreUtil.putInt(getNumRecords()+1, dbHeader, RS3_NUM_LIVE);
                RecordStoreUtil.putInt(getVersion()+1, dbHeader, RS4_VERSION);
                RecordStoreUtil.putLong(System.currentTimeMillis(), dbHeader,
                        RS5_LAST_MODIFIED);

                // write out the changes to the db header
                dbFile.seek(RS2_NEXT_ID);
                dbFile.write(dbHeader, RS2_NEXT_ID, 3*4+8);
                // dbFile.commitWrite();
            } catch (java.io.IOException ioe) {
                throw new RecordStoreException("error writing new record "
                                               + "data");
            }

            // Return the new record id
            return recordId;
        }
    
public voidcloseRecordStore()
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.

exception
RecordStoreNotOpenException if the record store is not open
exception
RecordStoreException if a different record store-related exception occurred


        synchronized (rsLock) {
            try {
                // close native fd
                compactRecords();  // compact before close

                dbFile.close();

                dbIndex.close();
            } catch (java.io.IOException ioe) {
                throw new RecordStoreException("error closing .db file. "
						+ ioe);
            } finally {
                dbFile = null;
            }
        }
    
private voidcompactRecords()
Remove free blocks from the record store and compact records with data into as small a space in dbFile as possible. Operates from smallest to greatest offset in dbFile, copying data in chunks towards the beginning of the file, and updating record store meta-data as it progresses. Warning: This is a slow operation that scales linearly with dbFile size.

exception
RecordStoreNotOpenException if this record store is closed
exception
RecordStoreException if an error occurs during record store compaction


        // check if the db can be compacted
        if (RecordStoreUtil.getInt(dbHeader, RS7_FREE_SIZE) == 0) {
            // no free space to compact
            return;
        }

        byte[] header = new byte[BLOCK_HEADER_SIZE];
        int currentId = 0;
        int currentOffset = RecordStoreImpl.DB_HEADER_SIZE;
        int currentSize = 0;
        int moveUpNumBytes = 0;

        // search through the data blocks for a free block that is large enough
        while (currentOffset < getSize()) {
            // seek to the next offset
            dbFile.seek(currentOffset);

            // read the block header
            if (dbFile.read(header) != RecordStoreImpl.BLOCK_HEADER_SIZE) {
                // could not read the block
                throw new IOException();
            }

            currentId = RecordStoreUtil.getInt(header, 0);
            currentSize =
              RecordStoreUtil.calculateBlockSize(RecordStoreUtil.getInt(
                                                   header, 4));

            if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
                Logging.report(Logging.INFORMATION, LogChannels.LC_RMS,
                               "currentId = " + currentId +
                               " currentSize = " + currentSize);
            }

            // check if this is a free block or a record
            if (currentId < 0) {
                if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
                    Logging.report(Logging.INFORMATION, LogChannels.LC_RMS,
                                   "found free block at offset " +
                                   currentOffset);
                }

                // a free block, add to the moveUpNumBytes
                moveUpNumBytes += currentSize;

                // remove from the index
                dbIndex.removeBlock(currentOffset, header);
            } else {
                if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
                    Logging.report(Logging.INFORMATION, LogChannels.LC_RMS,
                                   "found record block at offset " +
                                   currentOffset);
                }

                // a record data block, check if it needs to be moved up
                if (moveUpNumBytes > 0) {

                    if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
                        Logging.report(Logging.INFORMATION, LogChannels.LC_RMS,
                                       "moveUpNumBytes = " + currentOffset);
                    }

                    int numMoved = 0;
                    while (numMoved < currentSize) {
                        int curRead = currentSize - numMoved;
                        if (curRead > COMPACT_BUFFER_SIZE) {
                            curRead = COMPACT_BUFFER_SIZE;
                        }

                        dbFile.seek(currentOffset + numMoved);
                        curRead = dbFile.read(compactBuffer, 0, curRead);
                        if (curRead == -1) {
                            throw new IOException();
                        }

                        dbFile.seek(currentOffset + numMoved - moveUpNumBytes);
                        dbFile.write(compactBuffer, 0, curRead);
                        // dbFile.commitWrite();
                        numMoved += curRead;
                    }

                    dbIndex.updateBlock(currentOffset - moveUpNumBytes, header);
                }
            }

            // added the block size to the currentOffset
            currentOffset += currentSize;
        }

        // check if the db file can be truncated
        if (moveUpNumBytes > 0) {
            RecordStoreUtil.putInt(
                RecordStoreUtil.getInt(dbHeader, RS6_DATA_SIZE) -
                    moveUpNumBytes, dbHeader, RS6_DATA_SIZE);
            RecordStoreUtil.putInt(0, dbHeader, RS7_FREE_SIZE);
            dbFile.seek(RS6_DATA_SIZE);
            dbFile.write(dbHeader, RS6_DATA_SIZE, 4+4);
            // dbFile.commitWrite();

            dbFile.truncate(getSize());

            if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
                Logging.report(Logging.INFORMATION, LogChannels.LC_RMS,
                               "compactRecords, truncate to size " +
                               getSize());
            }
        }
    
public AbstractRecordStoreFilecreateIndexFile(int suiteId, java.lang.String name)
Creates data base index file associated with this record store

param
suiteId unique ID of the suite that owns the store
param
name a string to name the index file
return
data base index file
exception
IOException if failed to create a file

        return new RecordStoreFile(suiteId, name,
                                   AbstractRecordStoreFile.IDX_EXTENSION);
    
public voiddeleteRecord(int recordId)
The record is deleted from the record store. The recordId for this record is NOT reused.

param
recordId the ID of the record to delete
exception
RecordStoreNotOpenException if the record store is not open
exception
InvalidRecordIDException if the recordId is invalid
exception
RecordStoreException if a general record store exception occurs
exception
SecurityException if the MIDlet has read-only access to the RecordStore

        synchronized (rsLock) {
            try {
                byte[] header = new byte[BLOCK_HEADER_SIZE];
                int blockOffset = dbIndex.getRecordHeader(recordId, header);

                // free the block
                freeBlock(blockOffset, header);

                // update the db index
                dbIndex.deleteRecordIndex(recordId);

                // update the db header
                RecordStoreUtil.putInt(getNumRecords()-1, dbHeader, RS3_NUM_LIVE);
                RecordStoreUtil.putInt(getVersion()+1, dbHeader, RS4_VERSION);
                RecordStoreUtil.putLong(System.currentTimeMillis(), dbHeader,
                        RS5_LAST_MODIFIED);

                // save the updated db header
                dbFile.seek(RS3_NUM_LIVE);
                dbFile.write(dbHeader, RS3_NUM_LIVE, 2*4+8);
                // dbFile.commitWrite();

            } catch (java.io.IOException ioe) {
                throw new RecordStoreException("error updating file after" +
                                               " record deletion");
            }
        }
    
public static voiddeleteRecordStore(com.sun.midp.security.SecurityToken token, int suiteId, 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.

param
token security token for authorization
param
suiteId ID of the MIDlet suite that owns the record store
param
recordStoreName the MIDlet suite unique record store to delete
exception
RecordStoreException if a record store-related exception occurred
exception
RecordStoreNotFoundException if the record store could not be found


                                                                                                                                                               
        
                                          
                                          
           

        token.checkIfPermissionAllowed(Permissions.MIDP);

        // check if file exists and delete it
        if (RecordStoreUtil.exists(suiteId, recordStoreName,
                                   RecordStoreFile.DB_EXTENSION)) {
            boolean success = RecordStoreIndex.deleteIndex(
                suiteId, recordStoreName);
            RecordStoreUtil.deleteFile(suiteId,
                recordStoreName, RecordStoreFile.DB_EXTENSION);

            if (!success) {
                throw new RecordStoreException("deleteRecordStore " +
                                               "failed");
            }
        } else {
            throw new RecordStoreNotFoundException("deleteRecordStore " +
                                                   "error: file " +
                                                   "not found");
        }
    
private voidfreeBlock(int blockOffset, byte[] header)
Mark the block at the given offset in db file as free.

param
blockOffset the offset in db file to the block to free
param
header the header of the block to free
exception
IOException if there is an error accessing the db file

        int dataSize = RecordStoreUtil.getInt(header, 4);

        // calculate the size of the block
        int blockSize = RecordStoreUtil.calculateBlockSize(dataSize);

        if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
            Logging.report(Logging.INFORMATION, LogChannels.LC_RMS,
                           "freeBlock(" + blockOffset + ") size = " +
                           blockSize);
        }

        // mark the block as free
        RecordStoreUtil.putInt(-1, header, 0);
        RecordStoreUtil.putInt(blockSize - BLOCK_HEADER_SIZE, header, 4);

        // save the updated block header
        writeBlock(blockOffset, header, null, 0, 0);

        // add to the db free size
        RecordStoreUtil.putInt(RecordStoreUtil.getInt(dbHeader, RS7_FREE_SIZE) + blockSize,
               dbHeader, RS7_FREE_SIZE);
        dbFile.seek(RS7_FREE_SIZE);
        dbFile.write(dbHeader, RS7_FREE_SIZE, 4);
        // dbFile.commitWrite();
    
public intgetAuthMode()
Get the authorization mode for this record store.

return
authorization mode

        return RecordStoreUtil.getInt(dbHeader, RS1_AUTHMODE);
    
public AbstractRecordStoreFilegetDbFile()
Returns data base file associated with this record store

return
data base file

        return dbFile;
    
public longgetLastModified()
Returns the last time the record store was modified, in the format used by System.currentTimeMillis().

return
the last time the record store was modified, in the format used by System.currentTimeMillis()

        return RecordStoreUtil.getLong(dbHeader, RS5_LAST_MODIFIED);
    
public intgetNextRecordID()
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().

return
the recordId of the next record to be added to the record store

        return RecordStoreUtil.getInt(dbHeader, RS2_NEXT_ID);
    
public intgetNumRecords()
Returns the number of records currently in the record store.

return
the number of records currently in the record store

        return RecordStoreUtil.getInt(dbHeader, RS3_NUM_LIVE);
    
public intgetRecord(int recordId, byte[] buffer, int offset)
Returns the data stored in the given record.

param
recordId the ID of the record to use in this operation
param
buffer the byte array in which to copy the data
param
offset the index into the buffer in which to start copying
exception
RecordStoreNotOpenException if the record store is not open
exception
InvalidRecordIDException if the recordId is invalid
exception
RecordStoreException if a general record store exception occurs
exception
ArrayIndexOutOfBoundsException if the record is larger than the buffer supplied
return
the number of bytes copied into the buffer, starting at index offset
see
#setRecord

        synchronized (rsLock) {
            try {
                byte[] header = new byte[BLOCK_HEADER_SIZE];
                int blockOffset = dbIndex.getRecordHeader(recordId, header);

                int dataSize = RecordStoreUtil.getInt(header, 4);

                dbFile.seek(blockOffset+BLOCK_HEADER_SIZE);
                return dbFile.read(buffer, offset, dataSize);
            } catch (java.io.IOException ioe) {
                throw new RecordStoreException("error reading record data");
            }
        }
    
public byte[]getRecord(int recordId)
Returns a copy of the data stored in the given record.

param
recordId the ID of the record to use in this operation
exception
RecordStoreNotOpenException if the record store is not open
exception
InvalidRecordIDException if the recordId is invalid
exception
RecordStoreException if a general record store exception occurs
return
the data stored in the given record. Note that if the record has no data, this method will return null.
see
#setRecord

        synchronized (rsLock) {
            try {
                byte[] header = new byte[BLOCK_HEADER_SIZE];
                int blockOffset = dbIndex.getRecordHeader(recordId, header);

                int dataSize = RecordStoreUtil.getInt(header, 4);
                if (dataSize == 0) {
                    return null;
                }

                byte[] buffer = new byte[dataSize];

                dbFile.seek(blockOffset+BLOCK_HEADER_SIZE);
                dbFile.read(buffer);

                return buffer;
            } catch (java.io.IOException ioe) {
                throw new RecordStoreException("error reading record data");
            }
        }
    
public int[]getRecordIDs()
Returns all of the recordId's currently in the record store.

return
an array of the recordId's currently in the record store or null if the record store is closed.

        synchronized (rsLock) {
            return dbIndex.getRecordIDs();
        }
    
public intgetRecordSize(int recordId)
Returns the size (in bytes) of the MIDlet data available in the given record.

param
recordId the ID of the record to use in this operation
return
the size (in bytes) of the MIDlet data available in the given record
exception
RecordStoreNotOpenException if the record store is not open
exception
InvalidRecordIDException if the recordId is invalid
exception
RecordStoreException if a general record store exception occurs

        synchronized (rsLock) {
            byte[] header = new byte[BLOCK_HEADER_SIZE];

            try {
                dbIndex.getRecordHeader(recordId, header);
            } catch (java.io.IOException ioe) {
                throw new RecordStoreException("error reading record data");
            }

            return RecordStoreUtil.getInt(header, 4);
        }
    
public intgetSize()
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.

return
the size of the record store in bytes

        return DB_HEADER_SIZE + RecordStoreUtil.getInt(dbHeader, RS6_DATA_SIZE);
    
public intgetSizeAvailable()
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.

return
the amount of additional room (in bytes) available for this record store to grow

        int fileSpace = dbFile.spaceAvailable(suiteId) -
                        BLOCK_HEADER_SIZE - DB_HEADER_SIZE;
        int limitSpace = RMSConfig.STORAGE_SUITE_LIMIT -
                         RecordStoreUtil.getInt(dbHeader, RS6_DATA_SIZE) -
                         BLOCK_HEADER_SIZE - DB_HEADER_SIZE;

        int rv = (fileSpace < limitSpace) ? fileSpace : limitSpace;
        return (rv < 0) ? 0 : rv;
    
public intgetVersion()
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.

return
the current record store version

        return RecordStoreUtil.getInt(dbHeader, RS4_VERSION);
    
public static java.lang.String[]listRecordStores(com.sun.midp.security.SecurityToken token, int suiteId)
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.

param
token security token for authorization
param
suiteId ID of the MIDlet suite that owns the record store
return
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.

        token.checkIfPermissionAllowed(Permissions.MIDP);

        return RecordStoreFile.listRecordStores(suiteId);
    
public static com.sun.midp.rms.RecordStoreImplopenRecordStore(com.sun.midp.security.SecurityToken token, int suiteId, 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 RecordStoreImpl object.

param
token security token for authorization
param
suiteId ID of the MIDlet suite that owns the record store
param
recordStoreName the MIDlet suite unique name for the record store, consisting of between one and 32 Unicode characters inclusive.
param
createIfNecessary if true, the record store will be created if necessary
return
RecordStore object for the record store
exception
RecordStoreException if a record store-related exception occurred
exception
RecordStoreNotFoundException if the record store could not be found
exception
RecordStoreFullException if the operation cannot be completed because the record store is full
exception
IllegalArgumentException if recordStoreName is invalid


        token.checkIfPermissionAllowed(Permissions.MIDP);

        return new RecordStoreImpl(suiteId, recordStoreName,
                                   createIfNecessary);
    
public voidsetMode(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.

param
authmode the mode under which to check or create access. Must be one of AUTHMODE_PRIVATE or AUTHMODE_ANY.
param
writable true if the RecordStore is to be writable by other MIDlet suites that are granted access
exception
RecordStoreException if a record store-related exception occurred
exception
SecurityException if this MIDlet Suite is not allowed to change the mode of the RecordStore
exception
IllegalArgumentException if authmode is invalid


        synchronized (rsLock) {
            int newAuthMode = authmode;
            if ((authmode == RecordStore.AUTHMODE_ANY) &&
                    (writable == false)) {
                newAuthMode = AUTHMODE_ANY_RO;
            }

            RecordStoreUtil.putInt(newAuthMode, dbHeader, RS1_AUTHMODE);

            try {
                // write out the changes to the db header
                dbFile.seek(RS1_AUTHMODE);
                dbFile.write(dbHeader, RS1_AUTHMODE, 4);
                // dbFile.commitWrite();
            } catch (java.io.IOException ioe) {
                throw new RecordStoreException("error writing record " +
                                               "store attributes");
            }
        }
    
public voidsetRecord(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.

param
recordId the ID of the record to use in this operation
param
newData the new data to store in the record
param
offset the index into the data buffer of the first relevant byte for this record
param
numBytes the number of bytes of the data buffer to use for this record
exception
RecordStoreNotOpenException if the record store is not open
exception
InvalidRecordIDException if the recordId is invalid
exception
RecordStoreException if a general record store exception occurs
exception
RecordStoreFullException if the operation cannot be completed because the record store has no more room
exception
SecurityException if the MIDlet has read-only access to the RecordStore
see
#getRecord


        synchronized (rsLock) {
            try {
                byte[] header = new byte[BLOCK_HEADER_SIZE];
                int blockOffset = dbIndex.getRecordHeader(recordId, header);

                int oldBlockSize =
                  RecordStoreUtil.calculateBlockSize(RecordStoreUtil.getInt(
                                                       header, 4));
                int newBlockSize = RecordStoreUtil.calculateBlockSize(numBytes);
                if (newBlockSize <= oldBlockSize) {
                    // reuse the old block
                    splitBlock(blockOffset, header, newData, offset, numBytes);
                } else {
                    // free the old record data
                    freeBlock(blockOffset, header);

                    // add a block that contains the new record data
                    addBlock(recordId, newData, offset, numBytes);
                }

                // update the db header
                RecordStoreUtil.putInt(getVersion()+1, dbHeader, RS4_VERSION);
                RecordStoreUtil.putLong(System.currentTimeMillis(), dbHeader,
                        RS5_LAST_MODIFIED);

                // write out the changes to the db header
                dbFile.seek(RS4_VERSION);
                dbFile.write(dbHeader, RS4_VERSION, 4+8);
                // dbFile.commitWrite();
            } catch (java.io.IOException ioe) {
                throw new RecordStoreException("error setting record data");
            }
        }
    
private voidsplitBlock(int blockOffset, byte[] header, byte[] newData, int offset, int numBytes)
Set the record in the block to the data passed in and adds any remaining space to the free list.

param
blockOffset the offset in db file to the block to split
param
header the header of the block to split
param
newData the new data to store in the record
param
offset the index into the data buffer of the first relevant byte for this record
param
numBytes the number of bytes of the data buffer to use for this record
exception
IOException if there is an error accessing the db file


        if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
            Logging.report(Logging.INFORMATION, LogChannels.LC_RMS,
                           "splitBlock("+RecordStoreUtil.getInt(header, 0) +
                           ") old numBytes = " + RecordStoreUtil.getInt(header, 4) +
                           " new numBytes=" + numBytes);
        }

        // calculate the size of the block
        int oldBlockSize =
          RecordStoreUtil.calculateBlockSize(RecordStoreUtil.getInt(
                                               header, 4));

        // calculate the size of the block
        int newBlockSize = RecordStoreUtil.calculateBlockSize(numBytes);

        // update the block header
        RecordStoreUtil.putInt(numBytes, header, 4);

        // seek to the location and write the block and header
        writeBlock(blockOffset, header, newData, offset, numBytes);

        // check if there is any left over free space
        int freeSize = oldBlockSize - newBlockSize - BLOCK_HEADER_SIZE;
        if (freeSize >= 0) {
            // update the block header and free block of extra space
            RecordStoreUtil.putInt(freeSize, header, 4);
            freeBlock(blockOffset+newBlockSize, header);
        }
    
private voidwriteBlock(int blockOffset, byte[] header, byte[] data, int offset, int numBytes)
Writes a block to the db file at the given offset.

param
blockOffset the offset in db file to write the block
param
header the header of the block to write
param
data the new data to store in the record
param
offset the index into the data buffer of the first relevant byte for this record
param
numBytes the number of bytes of the data buffer to use for this record
exception
IOException if there is an error accessing the db file


        int remainder;
        dbFile.seek(blockOffset);
        dbFile.write(header);
        if (data != null && numBytes > 0) {
            dbFile.write(data, offset, numBytes);
            remainder = numBytes % BLOCK_HEADER_SIZE;
            if (remainder != 0) {
                // DB_SIGNATURE used here as meaningless pad bytes
                dbFile.write(DB_SIGNATURE, 0, BLOCK_HEADER_SIZE - remainder);
            }
        }
        // flush the writes
        // dbFile.commitWrite();
        // update the index
        dbIndex.updateBlock(blockOffset, header);