CERCA SITEMAP FEED RSS 1280
Ultimo aggiornamento: 30 Agosto 2009

Record Management System

Motivi di sicurezza impediscono di fatto ad applicazioni J2ME di accedere al filesystem del dispositivo che si sta utilizzando.
Il profilo MIDP garantisce comunque la persistenza dei dati attraverso il Record Management System (RMS) un semplice database accessibile tramite opportune operazioni (scrittura, lettura, modifica e cancellazione) e costituito da record.
L’insieme dei record logicamente connessi costituisce un RecordStore, che viene solitamente memorizzato nella stessa directory in cui risiede la MIDlet che lo ha creato e che può essere paragonato alle tabelle dei DBMS (Database Management System).
Un RecordStore, identificato univocamente da un nome case-sensitive di lunghezza massima pari a 32 caratteri unicode, costituisce quindi l’unità informativa più grande mentre il record l’unità informativa più piccola che è possibile trattare.
Struttura del RecordStore
L’immagine a lato mostra l’organizzazione del RecordStore in Record ovvero le unità informative più piccole che è possibile trattare attraverso il Record Management System.

Mentre un RecordStore può essere paragonato alla tabella (Table) di un DBMS i Record possono essere paragonati alle righe di quest’ultima.
Il package javax.microedition.rms contiene classi ed interfacce per:
  • la gestione del RecordStore
  • il monitoraggio del RecordStore
  • l’implementazione di filtri e comparatori di record
  • l’accesso sequenziale ai record
Di seguito verranno analizzate nel dettaglio le caratteristiche e le funzionalità di:
  • la classe RecordStore che è la più importante in quanto permette di compiere tutte le elaborazioni elementari sui dati
  • l’interfaccia RecordListener che definisce dei metodi che catturano i cambiamenti di stato del Database
  • le interfaccia RecordFilter e RecordComparator necessarie nella creazione di filtri e funzioni di ordinamento di record
  • l’interfaccia RecordEnumeration che fornisce i metodi per un accesso sequenziale ai record

La classe RecordStore

La classe RecordStore è certamente la più importante del package in quanto permette di gestire i RecordStore creati e di compiere operazioni di lettura, scrittura, modifica e cancellazione sui dati.
Ciascun RecordStore contiene un header che memorizza informazioni relative allo "stato" del database ed un’area dati che contiene fisicamente i record memorizzati.

In particolare l’header memorizza informazioni su:
  • il nome del RecordStore
  • il numero di record presenti nel RecordStore
  • il numero della versione del RecordStore
  • la data di ultima modifica
  • il primo blocco libero e quindi disponibile per un ulteriore inserimento
  • lo spazio occupato in byte
  • lo spazio disponibile in byte
Queste informazioni possono essere recuperate attraverso i seguenti metodi:
  • getName() restituisce il nome del RecordStore
  • getNumRecords() restituisce il numero di record presenti e viene incrementato e decrementato a seconda degli inserimenti e delle cancellazioni che vengono effettuate.
  • getVersion() restituisce la versione del RecordStore: tipicamente all’atto della creazione ogni RecordStore ha un numero di versione pari a 0, ad ogni successiva modifica tale numero viene incrementato.
  • getLastModified() restituisce la data di ultima modifica che viene aggiornata di pari passo al numero di versione.
  • getNextRecordId() restituisce l’identificativo univoco del primo blocco libero all’interno del RecordStore
  • getSize() restituisce lo spazio occupato espresso in byte
  • getSizeAvailable() restituisce lo spazio disponibile (in byte) per l’inserimento di eventuali nuovi record.
Dal momento che una MIDlet può creare più RecordStore il metodo listRecordStores() recupera un array con tutti i nomi dei RecordStore memorizzati nella directory della MIDlet stessa.
La gestione dell’area dati di un RecordStore avviene nel seguente modo:
  • la lettura di un record sfrutta il metodo getRecord(int recordId) che vuole come parametro l’id del record da recuperare e restituisce un array di byte corrispondente al contenuto del record stesso: è possibile preventivamente conoscere la dimensione di un record in termini di numero di byte sfruttando il metodo getRecordSize(int recordId)
    byte[] record = recordstore.getRecord(1);
    
  • la scrittura di un record utilizza il metodo addRecord(byte[] data, int offset, int numBytes) che vuole come parametri un array di byte, un offset ed il numero di byte che definiscono il record da inserire e restituisce l’id dello stesso.
    String stringa = new String("valore da memorizzare");
    byte[] record = stringa.getBytes();
    recordstore.addRecord(record, 0, record.length);
    
  • per modificare un record si utilizza il metodo setRecord(int recordId, byte[] data, int offset, int numBytes) passando come parametri l’id del record da modificare e un array con offset e numero di bytes corrispondenti al nuovo contenuto dello stesso.
    String stringa = new String("nuovo valore da memorizzare");
    byte[] record = stringa.getBytes();
    recordstore.setRecord(1,record, 0, record.length);
    
  • la cancellazione avviene tramite deleteRecord(int recordId) che utilizza come unico parametro l’id del record da cancellare.
    recordstore.deleteRecord(1);
    
Naturalmente prima di compiere operazioni sui dati è necessario aprire il RecordStore.
L’apertura di un RecordStore avviene tramite il metodo openRecordStore(String nomeRecordStore, boolean createIfNecessary) che utilizza il nome del RecordStore da aprire ed un booleano che una volta settato a true indica di creare il RecordStore qualora non sia già esistente.
La chiusura utilizza il metodo closeRecordStore() mentre la cancellazione utilizza deleteRecordStore(String nomeRecordStore) in cui occorre specificare il nome del RecordStore da cancellare.
Infine è possibile specificare alcune caratteristiche di sicurezza del database mediante il metodo setMode(int authmode, boolean writable) che permette di rendere visibile il RecordStore ad altre MIDlet oltre a quella che lo ha generato e di specificare se queste possono accedere al RecordStore in sola lettura o anche in scrittura.

Monitoraggio del RecordStore

Implementando l’interfaccia RecordListener è possibile monitorare i cambiamenti del RecordStore creando un comportamento simile a quello dei trigger nei DBMS più potenti (tipo Oracle).
L’intefaccia RecordListener definisce tre metodi che permettono di catturare gli eventi di aggiunta (recordAdded(RecordStore recordStore, int recordId)), rimozione (recordDeleted(RecordStore recordStore, int recordId)) e modifica (recordChanged(RecordStore recordStore, int recordId)) di record all’interno di un RecordStore.
Il seguente esempio mostra un RecordListener che potrebbe essere utilizzato per far apparire un messaggio sul dispositivo J2ME ogni volta che viene effettuata una modifica al RecordStore sul quale si sta lavorando.
...

public class Monitor extends RecordListener
{
   public void recordDeleted(RecordStore recordStore, int recordId)
   {
      Alert alert = new Alert("Record cancellato.", icona, Alert.INFO);
      alert.setTimeout(Alert.FOREVER);
      display.setCurrent(alert,form);
   }

   public void recordAdded(RecordStore recordStore, int recordId)
   {
      Alert alert = new Alert("Record aggiunto.", icona, Alert.INFO);
      alert.setTimeout(Alert.FOREVER);
      display.setCurrent(alert,form);
   }

   public void recordChanged(RecordStore recordStore, int recordId)
   {
      Alert alert = new Alert("Record modificato.", icona, Alert.INFO);
      alert.setTimeout(Alert.FOREVER);
      display.setCurrent(alert,form);
   }
}

...

recordstore.addRecordListener(new Monitor());

...
Una volta definiti tali metodi è opportuno collegare il RecordListener al RecordStore attraverso il metodo addRecordListener(RecordListener listener).
Analogamente sarà possibile rimuovere un RecordListener da un RecordStore mediante il metodo removeRecordListener(RecordListener listener).

Filtraggio ed ordinamento di record

Il metodo getRecord(int recordId) permette di recuperare un record a partire dal suo identificativo.
Sfruttando tale metodo è possibile accedere sequenzialmente a tutti i record tramite un ciclo iterativo incrementando di volta in volta l’id di accesso.
Tale modalità di accesso tuttavia genera delle eccezioni nei casi in cui ad un determinato id non corrisponda alcun record in memoria (ad esempio potrei inserire nove record e poi cancellare il quarto, in questo caso il ciclo da uno a nove genererebbe un’eccezione durante l’accesso al quarto record).
Per queste ragioni la classe RecordStore fornisce il metodo enumerateRecords(RecordFilter filter, RecordComparator comparator, boolean keepUpdated) che consente di accedere ai record memorizzati in un modo più rapido ed efficiente.

Tale metodo restituisce un RecordEnumeration che consente un accesso sequenziale ai record mediante i seguenti metodi:
  • hasNextElement(): restituisce true se sono presenti record successivi a quello corrente
  • hasPreviousElement(): restituisce true se sono presenti record precedenti a quello corrente
  • nextRecord(): restituisce il record successivo a quello corrente
  • previousRecord(): restituisce il record che precede quello corrente
  • nextRecordId(): restituisce l’id del record successivo a quello corrente
  • previousRecordId(): restituisce l’id del record che precede quello corrente
Il metodo keepUpdate(boolean keepUpdated) permette inoltre di effettuare l’aggiornamento automatico del RecordEnumeration a seguito delle modifiche apportate sul RecordStore.

enumerateRecords(RecordFilter filter, RecordComparator comparator, boolean keepUpdated) della classe RecordStore utilizza tre parametri:
  • un oggetto RecordFilter che effettua un filtraggio dei record presenti in base ad opportuni requisiti di ricerca
  • un oggetto RecordComparator che ordina i record filtrati dal RecordFilter secondo un’opportuna funzione di comparazione
  • un booleano che permette di impostare la proprietà keepUpdate del RecordEnumeration ottenuto
Per definire un RecordFilter occorre implementare l’interfaccia omonima e quindi definire il metodo matches(byte[] record): tale metodo conterrà di fatto la logica di "filtraggio" del record in esame restituendo true se il record è conforme alle regole definite e false altrimenti.

Esempio di filtraggio di tutti i record che iniziano per "Z"
public class Filtro implements RecordFilter
{
   public boolean matches(byte[] recordCandidate)
   {
      String stringa = new String(recordCandidate);
      return (stringa.startsWith("Z"));
   }
}
Allo stesso modo per implementare una logica di ordinamento dei record occorre estendere l’interfaccia RecordComparator e quindi definire il metodo compare(byte[] record1, byte[] record2): questo infatti effettua il confronto fra il record1 ed il record2 secondo la logica da noi stabilita e dovrà restituire RecordComparator.PRECEDES se record1 precede record2, RecondComparator.EQUIVALENT se record1 e record2 sono equivalenti in termini di posizionamento e RecordComparator.FOLLOWS se record1 deve seguire record2.
Esempio di ordinamento in base alla lunghezza (numero di caratteri)
public class Ordinatore implements RecordComparator
{
   public int compare(byte[] record1, byte[] record2)
   {
      if (record1.length>record2.length)
      {
         return RecordComparator.PRECEDES;
      }
      else
      if (record1.length<record2.length)
      {
         return RecordComparator.FOLLOWS;
      }
      else
      return RecordComparator.EQUIVALENT;
   }
}