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.