Dienstag, 1. März 2011

 

Binärdateien bearbeiten mit Java (PR: 5A, 5B)

Entwickeln Sie sie eine Klasse Data, die das Interface DB implementiert. Die Klasse Data soll die Methoden zum Bearbeiten (Lesen, Schreiben) binärer Dateien nach dem unten beschriebenen Datenformat bereitstellen.

package data;
/**
 * RuD-Übung 2011
 *
 * data.DB.java
 * @author (c) 2011, Harald R. Haberstroh
 * 1.3.2011
 */


import java.rmi.Remote;
import java.rmi.RemoteException;

/**
 * @author Harald R. Haberstroh (hh)
 * 
 */
public interface DB extends Remote {
  /**
   * Sperre den gegebenen Datensatz. Ist der Datensatz bereits gesperrt, dann
   * wait().
   * 
   * @param recNo
   *          Nummer des zu sperrenden Datensatzes.
   * @return Sperr-Identifikation.
   * @throws RemoteException
   * @throws RecordNotFoundException
   *           ungültige Datensatznummer
   */
  public long lock(int recNo) throws RemoteException, RecordNotFoundException;

  /**
   * Hebe Sperre wieder auf. notify().
   * 
   * @param recNo
   *          Nummer des gesperrten Datensatzes
   * @param lockCookie
   *          Sperr-Identfikation.
   * @throws RemoteException
   * @throws RecordNotFoundException
   *           ungültige Datensatznummer
   * @throws SecurityException
   *           ungültiges lockCookie oder nicht gelockt
   */
  public void unlock(int recNo, long lockCookie) throws RemoteException,
      RecordNotFoundException, SecurityException;

  /**
   * lege neuen Datensatz an.
   * 
   * @param record
   *          Neuer Datensatz
   * @return Nummer des neuen Datensatzes
   * @throws RemoteException
   */
  public int create(Record record) throws RemoteException;

  /**
   * lösche Datensatz. Sperre wird aufgehoben, notify();
   * 
   * @param recNo
   *          Nummer des Datensatzes.
   * @param lockCookie
   *          Sperr-Identfikation.
   * @throws RemoteException
   * @throws RecordNotFoundException
   *           ungültige Datensatznummer
   * @throws SecurityException
   *           ungültiges lockCookie oder nicht gelockt
   */
  public void delete(int recNo, long lockCookie) throws RemoteException,
      RecordNotFoundException, SecurityException;

  /**
   * ändere Datensatz (überschreibe mit dem gegebenen Datensatz).
   * 
   * @param record
   *          neuer Datensatz
   * @param recNo
   *          Nummer des gesperrten Datensatzes
   * @param lockCookie
   *          Sperr-Identfikation.
   * @throws RemoteException
   * @throws RecordNotFoundException
   *           ungültige Datensatznummer
   * @throws SecurityException
   *           ungültiges lockCookie oder nicht gelockt
   */
  public void update(Record record, int recNo, long lockCookie)
      throws RemoteException, RecordNotFoundException, SecurityException;

  /**
   * suche Datensätze.
   * 
   * @param criteria
   *          String-Array mit Kriterien, null matcht alles, sonst
   *          Stringrepräsentation des Suchkriteriums (entspricht den Daten in
   *          Record).
   * @return Array von Recordnummern
   * @throws RemoteException
   */
  public int[] find(String[] criteria) throws RemoteException;

  /**
   * lese Datensatz.
   * 
   * @param recNo
   *          Nummer des zu lesenden Datensatzes.
   * @return Record
   * @throws RemoteException
   * @throws RecordNotFoundException
   *           Datensatz existiert nicht.
   */
  public Record read(int recNo) throws RemoteException, RecordNotFoundException;
}
Die Klasse Data muss zusätzlich einen Konstruktor Data(String dateiname) bereitstellen, der die Datei dateiname (z.B. "machines.db") öffnet und zur Bearbeitung mit den im Interface DB definierten Methoden bereitstellt. Der Konstruktor muss die nötigen Exceptions werfen, wenn die Datei nicht geöffnet werden kann.
Der Parameter String[] criteria der Methode find() enthält das Suchkriterium, wobei jedem Feld ein String zugeordnet ist. Soll also nur nach einem Namen (siehe unten) gesucht werden, so muss criteria[1] den gesuchten Namen (bzw. einen Teil davon) enthalten. Alle anderen Elemente des Arrays müssen dann null sein. Soll nach mehreren Kriterien selektiert werden, so sind die entsprechenden Felder mit Werten zu belegen.
find() liefert dann ein Array mit den Indizes der passenden Datensätze.
Die Datensatznummer recNo ist immer der Index innerhalb der Datei!
Gelöschte Datensätze werden einfach als gelöscht markiert (s.u.) und müssen beim Anlegen neuer Datensätze zuerst verwendet werden (erst wenn kein gelöschter Datensatz existiert, wird die Datei vergrößert).
RecordNotFoundException wird immer dann geworfen, wenn recNo auf einen gelöschten Datensatz weist oder, wenn der Datensatz gar nicht existiert. Lösen Sie das Problem, dass nur mit der einfachen RecordNotFoundException das Dateiende nicht ermittelt werden kann, in geeigneter Weise.

Record muss in geeigneter Weise implementiert werden.

Datensätze sollen gesperrt werden können (die Klasse Data kann von mehreren parallelen Threads verwendet werden). Dazu soll die Methode lock() eine (beliebige) dem Datensatz zuordenbare Zahl (Lockcookie bzw. Sperr-Identifikation) liefern und den Satz sperren. Die Methoden, die eine Sperre benötigen müssen dann anhand dieses Lockcookies überprüfen, ob der Datensatz gesperrt ist und die entsprechende Operation ausführen. Ist der Datensatz nicht gesperrt oder das Lockcookie ungültig, so ist eine SecurityException zu werfen. Die Methode unlock() hebt die Sperre wieder auf. Nach einem delete() braucht man (kann man) kein unlock() machen.

Datenformat


Die Strings sind kodiert mit dem Encoding ISO-8859-1, d.h. jedes Zeichen ist einem Byte kodiert und es sind nur ASCII-Zeichen sowie die westeuropäischen Erweiterungen erlaubt. Es gibt Strings fixer Länge (werden mit Leerzeichen aufgefüllt) und Strings variabler Länge (werden wie C-Strings mit '\0' abgeschlossen) mit einer Obergrenze.
Die Datei mit den (Test-)Daten hat folgendes Format:
Die Datei beginnt mit einem Header:
4 Bytes Magic Cookie, Wert 4242
4 Offset in Bytes zu den eigentlichen Daten
2 Bytes Anzahl Felder pro Datensatz
Pro Feld gibt es folgenden Eintrag:
2 Bytes Länge Feldname
n Bytes Feldname
2 Bytes Länge Feldbeschreibung
n Bytes Feldbeschreibung für die Tabellenansicht
1 Byte  Datentyp: 'F' ... fix, 'V' ... variabel, 'C' ... ein Zeichen
2 Bytes Feldlänge
Nach den Feldbeschreibungen, d.h. ab dem Offset, finden sich die eigentlichen Datensätze gemäß der Beschreibungen im Header:
2 Bytes Flag Gelöscht (== 0 ... gelöscht, != 0 ... aktiv)
n Bytes Daten gemäß der Datensatzbeschreibung

Testdatei machines.db


Diese Datei enthält nur die Geräte (und deren grafische Koordinaten):
Feldname Feldbeschreibung (maximale) Länge Typ Bemerkung
id Geräte-Id 10 String '\0' terminiert z.B. "z23"
name Gerätename 100 String '\0' terminiert Gerätebeschreibung, z.B. "Laderoboter Halle B"
x x 5 String fixe Länge z.B. " 50" (vorne mit Leerzeichen aufgefüllt)
y y 5 String fixe Länge z.B. " 200"

Testen der Klasse Data


Schreiben Sie passende Tests um die Klasse Data zu testen. Der erste Test soll einfach die Datei machines.db in lesbarer Form auf der Konsole ausgeben.
Die weiteren Tests sollen natürlich die gesamte Funktionalität und das Locking testen.

Downloads



Abgabe


Nennen Sie das Projekt klasse-name-data also z.B. 5ad-meier-data und checken Sie es am CVS ein. Termin wird noch bekanntgegeben.

Wie geht's weiter


Die Datenklasse soll dann später in einem Client-/Server-System zum Einsatz kommen, wo mehrere grafische Clients eine Binärdatei bearbeiten sollen. Die Angabe dazu folgt...

Labels: , ,


Kommentare:

Kommentar veröffentlichen

Abonnieren Kommentare zum Post [Atom]





<< Startseite

This page is powered by Blogger. Isn't yours?

Abonnieren Posts [Atom]