Zum Hauptinhalt springen

Fehlerbehandlung

Die Java-Implementierung meldet Fehler über Exceptions. Alle Bibliotheksfehler sind Instanzen von OsfException, das von RuntimeException erbt — es sind also ungeprüfte Ausnahmen: kein throws-Klausel-Zwang, keine erzwungenen try/catch. Wer sie fangen will, fängt gezielt OsfException (oder eine ihrer Unterklassen); wer nicht, lässt sie bis zu einem zentralen Handler propagieren.

Davon getrennt steht der Best-Effort-Leser: Ein einzelner defekter oder abgeschnittener Datenblock beendet das Lesen nicht mit einer Ausnahme, sondern wird übersprungen und in ReaderStats gezählt. Nur strukturelle Fehler vor dem Block-Strom (Header, Metablock) und harte Vorbedingungsverletzungen werfen. Diese Trennung ist der Kern der Fehlerbehandlung: hart scheitern, solange die Datei prinzipiell uninterpretierbar ist — durchhalten, sobald nur einzelne Blöcke am Ende verloren sind.

Die Ausnahme-Hierarchie OsfException​

package com.optimeas.osf;

public class OsfException extends RuntimeException {
// Nachricht (und optional Ursache) — die Nachricht ist nur zur Anzeige
public static final class UnsupportedType extends OsfException { }
public static final class MalformedFile extends OsfException { }
public static final class UnknownHeaderToken extends OsfException { }
public static final class MetablockCrcMismatch extends OsfException { }
}

Regeln:

  • Auf den Typ verzweigen, die Nachricht nur anzeigen. getMessage() ist menschenlesbares Detail und nicht Teil der API — der Wortlaut darf sich ändern. Wer programmatisch reagieren will, prüft die Klasse (instanceof / catch-Reihenfolge).
  • Ein OsfException ohne spezifischere Unterklasse ist der Fall „gültige API, aber falsch benutzt oder I/O fehlgeschlagen" — vor allem aus den Writern.
  • Wo eine zugrunde liegende IOException die Ursache ist, wird sie als cause mitgeführt (getCause()), damit der Stacktrace vollständig bleibt.

Fehlerkatalog​

Beim Öffnen und Parsen (harte Fehler)​

Diese Ausnahmen entstehen, bevor überhaupt ein Nutzblock gelesen wird — die Datei ist als OSF nicht interpretierbar und wird komplett abgelehnt.

AusnahmeBedeutungTypische Quelle
MalformedFileStruktureller Defekt: kein wohlgeformter Magic-Header, unbekannter Versionsbezeichner, fehlendes Pflichtfeld im Metablock, nicht parsebare Zahl, ungültiges sizeoflengthvalue (≠ 2/4), unerwartetes Stromende, kein Zeilenumbruch im Header-Fenster, ungültiges JSON (OSF5) bzw. XML (OSF4). Trägt bei I/O-Ursache die IOException als cause.Header-, Metablock-, Block-Parser
UnknownHeaderTokenEin Magic-Header-Token, dessen Schlüssel die Bibliothek nicht kennt (Must-Understand-Regel). Bewusst von MalformedFile getrennt, damit ein unbekanntes Integritäts-/Erweiterungs-Token nicht als irreführender Zahlenformatfehler erscheint.Magic-Header
MetablockCrcMismatchDie im crc32c-Header-Token deklarierte CRC32C stimmt nicht mit den rohen Metablock-Bytes überein. Unter aktivem Integritätsprofil wird die Datei fail-closed abgelehnt — die Metadaten gelten als kompromittiert.Metablock-Verifikation
UnsupportedTypeDie Datei nutzt einen von der Spezifikation entfernten Datentyp (pair, triple, candata, gpsdata). Harte Ablehnung — das alte Payload-Layout ist aus einem aktuellen Build nicht reproduzierbar. Dient auch als Fehler beim Zugriff: ein typfalscher Getter auf DataChannel (z. B. Doubles von einem String-Kanal).Metablock-Parser, DataChannel

Beim Schreiben und bei API-Nutzung​

Die Writer (StreamingWriter, BlockWriter) werfen ein einfaches OsfException bei jeder verletzten Vorbedingung — sie signalisieren einen Programmierfehler, keinen Datendefekt:

AuslöserBeispiel-Bedeutung
Unbekannter KanalindexSample auf einen nicht deklarierten Kanal geschrieben
Typ-MismatchSchreib-Typ passt nicht zum deklarierten Kanal-Datentyp
Gemischte BlocktypenEin Kanal liefert äquidistante und zeitgestempelte Blöcke — spec-verboten
Falsche Lebenszyklus-PhaseSample geschrieben, obwohl der Writer noch konfiguriert oder bereits geschlossen ist
Kein Kanal / Länge inkonsistentbegin/writeTo ohne deklarierte Kanäle; timestamps.length ≠ values.length
Signiertes Profil angefordertDas ed25519-Profil (signiert) wird von dieser CRC-Level-Bibliothek beim Schreiben abgelehnt
I/O-FehlerDatei nicht öffenbar, Schreib-/force-Fehler — IOException als cause

Best-Effort-Leser: was bewusst kein Fehler ist​

Der Block-Stromleser hält an, wo eine Datei prinzipiell noch lesbar ist, statt zu werfen. Das Ergebnis wird in ReaderStats festgehalten, abrufbar über manager.stats():

SituationVerhalten
Datei endet mitten im Block (Stromausfall, Kürzung)Alle vollständigen Blöcke werden geliefert, stats.truncationSeen() wird true, die Iteration endet sauber
Defekter/garbelter BlockrumpfBest-Effort-Stopp an genau dieser Stelle: truncationSeen() = true, restliche gelesene Blöcke bleiben gültig
Unbekannter Kanalindex im Block-StromOhne Definition ist die Breite des Längenfelds unbekannt → Stopp (truncationSeen()) statt Rateversuch
Unbekannter zukünftiger DatentypKanal ist UNSUPPORTED; seine Blöcke werden per Länge übersprungen, alle anderen Kanäle laden normal
Frame-CRC eines Blocks stimmt nichtBlock wird verworfen, stats.blocksCrcFailed() zählt hoch, das Lesen läuft weiter
Signaturblock (reservierter Kanal 0xFFFE)Von dieser CRC-Level-Bibliothek nicht verifizierbar → übersprungen, stats.blocksSignatureSkipped() zählt hoch
Reservierte/leere BlocktypenAls übersprungen konsumiert; kein Fehler

Nur Fehler vor dem Block-Strom (Header, Metablock-CRC, Metablock-Parse) werfen — dort ist keine partielle Interpretation vertretbar.

Integritätsstatus — ReaderStats.verificationStatus()​

Nach dem Laden fasst stats.verificationStatus() das Integritätsergebnis in einem stabilen String zusammen (Vokabular aus der Spezifikation):

WertBedeutungEmpfohlene Reaktion
"none"Datei trägt kein IntegritätsprofilKeine — normale Verarbeitung
"crc_valid"Profil crc, jeder Block-CRC verifiziertDaten als integer behandeln
"invalid"Profil crc, mindestens ein Block-CRC schlug fehl (blocksCrcFailed() > 0)Warnen/ablehnen; die betroffenen Blöcke fehlen in den Daten
"signature_unverifiable"Signierte Datei (ed25519), die diese CRC-Level-Bibliothek nicht verifizieren kannNicht als „vertrauenswürdig signiert" ausweisen; Nutzdaten sind dennoch lesbar

verificationStatus() leitet sich rein aus dem deklarierten Profil und den Zählern ab — ein "invalid" bedeutet konkret, dass die CRC-fehlerhaften Blöcke übersprungen (nicht geliefert) wurden.

try/catch in der Praxis​

import com.optimeas.osf.*;

try {
DataManager mgr = DataManager.loadFromFile(Path.of("messung.osf"));

ReaderStats st = mgr.stats();
if (st.truncationSeen()) {
log.warn("Datei am Ende abgeschnitten — {} Blöcke gelesen", st.blocksRead());
}
switch (st.verificationStatus()) {
case "invalid" -> log.error("CRC-Fehler: {} Blöcke verworfen", st.blocksCrcFailed());
case "signature_unverifiable" -> log.warn("Signatur nicht prüfbar");
default -> { /* none / crc_valid — ok */ }
}

double[] werte = mgr.channelByName("temperatur")
.orElseThrow()
.asDoubles(); // wirft UnsupportedType bei Typ-Mismatch
} catch (OsfException.MetablockCrcMismatch e) {
// Metadaten kompromittiert — Datei ablehnen
} catch (OsfException e) {
// MalformedFile, UnknownHeaderToken, UnsupportedType, I/O …
log.error("OSF konnte nicht geladen werden: {}", e.getMessage(), e);
}

Praxisregeln:

  • Speziell vor allgemein fangen. Spezifische Unterklassen (MetablockCrcMismatch, UnknownHeaderToken) zuerst, dann OsfException als Auffangnetz.
  • Nach dem Laden stats() prüfen — ein erfolgreicher load heißt nicht, dass jeder Block ankam. Truncation und CRC-Ausfälle sind still und stehen nur in den Zählern.
  • Getter-Aufrufe auf DataChannel können UnsupportedType werfen, wenn der angeforderte Typ nicht zum Kanal passt — prüfe dataType() vorher oder fange gezielt.

Writer-Lebenszyklus​

Der StreamingWriter erzwingt eine Zustandsmaschine CONFIGURE → STREAMING → CLOSED. Ein Aufruf in der falschen Phase — Sample vor begin(), Schreiben nach close() — wirft OsfException. close() ist idempotent (ein zweiter Aufruf ist ein No-Op) und wechselt auch bei einem I/O-Fehler beim finalen Flush verlässlich nach CLOSED, sodass try-with-resources (der Writer ist AutoCloseable) die Datei nicht offen lässt.

Für weitere Details siehe Lesen, Schreiben, Architektur und das OSF-Formathandbuch. Zurück zur Java-Übersicht.

Dieses Dokument ist lizenziert unter CC BY 4.0. Namensnennung: optiMEAS GmbH und optiMEAS Switzerland GmbH.