Skip to content

Java File Persistence II

Wie schon im ersten Teil beschrieben, muss man trotz der Platform Unabhängikeit von Java etwas über die Zielsysteme wissen, um grobe Fehler zu vermeiden:
/** save data as UTF-8 string to file. */
saveFile(String data, File file) throws IOException, UnsupportedEncodingException {
  OutputStream out = null;
  bytes[] b = data.getBytes("UTF-8");
  try {
    out = new FileOutputStream(file);
    out.write(b);
  } catch(IOException ioe) {
    System.err.println("Cannot save data in UTF-8 to file " + file + ": " + ioe);
    throw ioe; // notify upper layer about problem
  } finally {
    silentClose(out); out = null;  // BANG, problem unterdrückt.
  }
}

silentClose(OutputStream out) {
  if (out != null)
    try { out.close(); } catch (Exception ignored) { }
}
In diesem Fall wird zwar richtigerweise im Fehlerfall der Output Stream geschlossen und die dabei eventuell auftretende Exception ignoriert, aber viel kritischer ist der Fall in dem im try block keine Exception aufgetreten ist, aber dafür dann das close() fehlschlägt. Es muss damit gerechnet werden dass im close() auf einem OutputStream eine IOException auftreten kann - sogar sehr häufig - der Grund dafür ist, dass zum einen der Stream einen flush() vor dem close() durchführen wird. Dazu kommt noch, dass die close() Methode der letzte Zeitpunkt ist, in dem ein IO Fehler gemeldet werden kann. Bei NFS ist es z.B. so, dass der client wartet bis der Server den Empfang bestätigt hat. Und dabei kann natürlich eine Menge schiefgehen. Auch quota Überschreitungen können zu einer IOException führen. Es hilft etwas als letztes Statement im try-block einen flush() durchzuführen, aber es besteht weiterhin ein Risiko dass close() fehlschlägt. Eine wichtige Regel lautet also: bei Streams in die geschrieben wird kann close() einen Fehler werfen, diese darf nicht ignoriert werden (sonst gehen Daten verloren)!
saveFile(String data, File file) throws IOException, UnsupportedEncodingException {
  OutputStream out = null;
  bytes[] b = data.getBytes("UTF-8");
  try {
    out = new FileOutputStream(file);
    out.write(b);
    out.close(); out = null;
  } catch(IOException ioe) {
    System.err.println("Cannot save data in UTF-8 to file " + file + ": " + ioe);
    throw ioe;
  } finally {
    silentClose(out); out = null;
  }
}
Im Falle von NFS ist obiger Code ausreichend. Nach dem close() ist es garantiert, dass die Daten permanent gespeichert sind. Bei lokalen Filesystemen wird diese Garantie von den gängigen Betriebsystemen NICHT gegeben. Die Daten können auch nach einem close() nur im lokalen Buffer Cache des Filesystems (RAM) liegen und erst nach einiger Zeit (typischerweise 5 Sekunden) an die Hardware Schicht übergeben werden. Dies ist zwar gut für die Performance, aber ein potentielles Fenster für Datenverlust - und eventuell Korruption des Anwendungszustandes. Wie dies zu vermeiden ist, betrachte ich im nächsten Teil (der kein Jahr auf sich warten lassen wird:)