# Java-Orakel
Das Orakel misst Javas **echtes** Verhalten, Fehler eingeschlossen. Der
TypeScript-Leser folgt Java, übernimmt aber keine Java-Fehler (Datenverlust,
Absturz, eigene Ausgabe nicht wieder lesbar, Tippfehler, Verstoß gegen den
Standard); jede solche Abweichung steht in den Tests namentlich mit Grund
(`JAVA_BUGS`). Was Java selbst schreibt — der Korpus — wird exakt wie in
Java gelesen.
Java-Filius selbst liest den Korpus (`corpus/`) und schreibt, was es gefunden
hat, nach `corpus/corpus.java.json`. **Java ist der Schiedsrichter für das
Dateiformat** — der Swift-Leser verliert in 10 von 23 Dateien Geräte (Plan
§13, M2).
```bash
npm run oracle:java # = tools/java-oracle/run.sh
```
Braucht JDK 17+, Maven und `xvfb-run`. Filius öffnet beim Laden sein
Hauptfenster (`Information.reset` → `JMainFrame`), deshalb läuft es an einem
virtuellen Bildschirm statt „headless".
* Gelesen wird so, wie Filius eine Datei öffnet: `SzenarioVerwaltung.laden`
entpackt das Archiv, kopiert die eigenen Anwendungen
(`projekt/anwendungen`) dorthin, wo Filius' Klassenlader sucht, und lädt
dann die Konfiguration — Javas Weg, kein Nachbau (seit 30.09.2026, M8).
`--network` lädt nur die Konfiguration über die private Methode
`netzwerkLaden` (per Reflection), wie vorher. Der Unterschied am Korpus:
In `anwendung_komplett.fls` und `multicast_server.fls` sind dann die
eigenen Anwendungen `Accumulator` und `Broadcast` installiert. Filius'
Klassenlader ist ein Singleton: Eine einmal geladene eigene Klasse bleibt
für die übrigen Dateien desselben Laufs geladen. Am Korpus stört das
nicht, denn keine Datei nutzt eine Klasse, die sie nicht mitbringt.
* Stand: `gitlab.com/filius1/filius` @ `6640354` (2.14.0), wird beim ersten
Lauf nach `.upstream/` geholt und gebaut.
* **Gewürfeltes:** Was nicht in der Datei steht, würfelt Filius zum Teil
(MAC-Adressen, die SSID eines Switches). Jede Datei wird deshalb **zweimal**
geladen; jeder Wert, der sich zwischen beiden Läufen unterscheidet,
erscheint als `null`. Zwei Läufe des Werkzeugs sind byte-gleich. (Bis
29.09.2026 suchte es die MAC im Dateitext und hielt dabei acht
kleingeschriebene für gewürfelt.)
* Bisher ausgelesen: Geräte (Klasse, Name, Beschriftung, Position,
Schnittstellen samt Funk-Eigenschaft `wireless`, bei Rechner/Notebook `useIPAsName`/`useMACAsName`), unter
`system` SSID, Lernzeit des Switches, RIP, manuelle Routen, die Firewall
von Router und Heimrouter, DHCP-Client und -Server, die statische
NAT-Tabelle des Heimrouters, die Verbindung eines Modems und das
Dateisystem (Inhalte als Länge und SHA-256, damit die Referenz klein
bleibt; bei Router und Heimrouter stehen vorn die Dateien, die Javas
Konstruktor aus eigenen Vorlagen erzeugt, dahinter die aus der Datei) und
die installierten Programme eines Rechners mit ihren Einstellungen (die
persönliche Firewall samt Regeln; je Programm `onItsSystem`: ob es am
Betriebssystem seines Knotens hängt) (Klassen, die Filius
nicht mitbringt — eigene Anwendungen im Archiv unter `projekt/anwendungen`
— fehlen, weil das Werkzeug nur `konfiguration.xml` lädt); E-Mail: Konten des Servers aus der Bean-Liste und aus `/mailserver/konten.txt` (`kontenLaden`), Ordner und Adressbuch des Clients und sein Konto aus `/konten.txt` (`laden`); ein Empfänger, den eine ältere Datei als Zeichenkette hält, samt dem, was Javas `AddressEntry(String)` daraus macht; Kabel (Enden als Gerät/Schnittstelle),
Doku-Elemente.
## Getter-Standardwerte
XMLEncoder teilt gleiche `Integer`/`Boolean`-Objekte. Ein Wert, der einmal als
Ergebnis eines Getters geschrieben wurde (``), taucht später nur noch als Verweis auf — etwa als
x-Koordinate eines Geräts. Daran scheitert der Swift-Leser (Plan §13, M2).
`GetterDefaults` erzeugt die betroffenen Klassen wie XMLDecoder (ohne
Argumente) und ruft den Getter auf; das Ergebnis steht in
`packages/fls/src/javaDefaults.json`. Gemessen, nicht geraten: Filius' Knopf
setzt `verticalTextPosition` auf 3 (BOTTOM), nicht auf Swings 0 (CENTER).
Dasselbe gilt für **statische Konstanten**: Die erste `0` einer Datei kann als
`` stehen, und jede
spätere `0` — etwa die Rot-, Grün- und Blauwerte einer schwarzen Doku-Farbe —
verweist darauf. Schreibweise im Aufruf: `klasse::FELD`, Ergebnis
`"SwingConstants.CENTER": 0`.
## `IP.ipCheck`
Java-Filius schickt jede IP-Adresse und Maske beim Laden durch
`IP.ipCheck`. `IpCheckCases` ruft es für jede Zeile von
`packages/fls/test/ip-check.cases.txt` auf (ASCII, Nicht-ASCII als
`\uXXXX`) und schreibt `ip-check.java.json`. Gemessen: leere Abschnitte
zählen nicht (`10..0.0.1`), Vorzeichen und führende Nullen gehen, ebenso
Ziffern jeder Schrift in der BMP (Thai, Devanagari, Vollbreite) — aber kein
Leerzeichen und kein 𝟏 (Ersatzpaar).
## `/dns/hosts`
Javas DNS-Server liest seine Einträge bei jeder Anfrage aus der Textdatei
`/dns/hosts`. `DnsHostsCases` legt für jede Zeile von
`packages/fls/test/dns-hosts.cases.txt` (eine ganze Datei je Zeile, ASCII,
Zeilenumbruch als `\u000A`) einen Rechner mit dieser Datei an und schreibt,
was `DNSServer.holeResourceRecords()` liefert, nach `dns-hosts.java.json`.
Gemessen: Felder trennt nur ein einzelnes Leerzeichen (Tab und NBSP nicht),
Zeilen nur LF; ein Name, den Javas Muster ablehnt (`1und1.de`, `müller.de`),
wird leer, der Eintrag bleibt trotzdem; Typen außer A/MX/NS (auch `a`,
`CNAME`, eine Kommentarzeile) gelten als gültig; eine TTL, die kein `int`
ist, wird still 3600.
## Was Java aus unseren Dateien lädt
`run.sh` lädt auch die Dateien unter `packages/fls/test/written/`, die
`topologyWriter.test.ts` mit `UPDATE_WRITTEN=1` schreibt, und unter
`packages/fls/test/exported/`, die `exportProject.test.ts` mit
`UPDATE_EXPORTED=1` schreibt (beide nicht eingecheckt), mit derselben
Lademethode wie den Korpus, und schreibt `written.java.json` bzw.
`exported.java.json` daneben, samt SHA-256 jeder Datei
(`--sha256`), damit der Test merkt, wenn der Befund nicht mehr zur Ausgabe
passt. Je Kabel steht auch `panel`: die Geräte, zwischen denen die
Oberfläche es zeichnet (`JCablePanel.ziel1/ziel2`) — im Korpus bei allen 183
Kabeln die Geräte, die es verbindet.
## Wie Java Base64 schreibt
`Base64Cases` ruft Filius' eigenes `Base64.encodeBytes` (iHarder 2.x) auf
Byte-Folgen von 19 Längen rund um die Zeilenlänge auf
(`packages/fls/test/base64.java.json`). Befund: nach je 76 Zeichen ein
`\n`, auch nach der letzten vollen Zeile, dann der aufgefüllte Rest ohne
Umbruch. So stehen Bilder in den Dateien, auch die, die Javas Konstruktoren
selbst anlegen.
## Was Java aus unserem XML macht
`BeansDump` liest jede `*.xml` unter `packages/fls/test/beans/` mit
`XMLDecoder` und beschreibt, was dabei herauskommt (Beans über ihre
Eigenschaften mit Lese- und Schreibmethode, Listen, Maps, Arrays,
`Rectangle`, `Color`; ein zweites Mal gesehenes Objekt als Verweis) —
`beans.java.json`. Fehler, die der Decoder sonst nur ins Log schreibt,
sammelt ein `ExceptionListener`. Zeichenketten stehen als ASCII mit
`\uXXXX`, weil ein einsames Surrogat in UTF-8 sonst zu „?“ würde.
## E-Mail-Kontodateien
`EmailFileCases server|client` legt für jede Zeile von
`packages/fls/test/email-server.cases.txt` bzw. `email-client.cases.txt`
einen Rechner mit dieser Datei an (`/mailserver/konten.txt` bzw.
`/konten.txt`) und schreibt, welche Konten `EmailServer.kontenLaden()` bzw.
`EmailAnwendung.laden()` daraus machen. Gemessen: Eine einzige Serverzeile
mit weniger als fünf Feldern lässt Java die ganze Datei verwerfen
(`NoSuchElementException` — die Simulation startet nicht); leere Felder
überspringt Java, statt sie zu zählen; eine Nachricht mit leerem Text geht
verloren, weil `split` leere Felder am Ende abschneidet. Beim Client gewinnt
die letzte Zeile, auch eine kaputte, die das Konto leert.
## DHCP zur Laufzeit
`DhcpRun` baut das Netz eines Kern-Szenarios (`packages/core/test/scenarios/*.scenario.json`:
Knoten, Kabel, Geräte, DHCP-Server und -Clients; die Schritte bleiben
unbeachtet) in Java-Filius nach und startet es wie der Aktionsmodus
(`starten()` jeder Systemsoftware). Nach der angegebenen Echtzeit schreibt es
nach `.java.json`, was jede Netzwerkkarte gesehen hat (Filius' eigener
Mitschnitt, der `Lauscher`: Zeit, MACs, ARP-Operation und -Adressen,
IP/UDP-Adressen und -Ports samt DHCP-Text), und die Adressen jedes Rechners
am Ende. Die MAC eines Ports ist dieselbe wie im Kern (`mac`, sonst
`stableGeneratedMacAddress`).
* Switches und DHCP-Server starten zuerst: Javas Client wartet auf jeden
aktiven Server, findet sie aber über die Knotenliste der Oberfläche, die das
Werkzeug nicht hat.
* `GUIContainer.getGUIContainer()` wird vor dem Start einmal aufgerufen. Im
echten Filius steht das Fenster schon; ohne das legen zwei Clients den
Singleton gleichzeitig an (nicht synchronisiert), und einer von ihnen
sendet nie etwas (gemessen am Szenario `dhcp-lan`).
* Die Zeiten schwanken von Lauf zu Lauf um einige zehn Millisekunden (echte
Threads), die Link-Local-Adresse würfelt Java mit `Math.random`. Die
Reihenfolge der Nachrichten eines Clients und ihr Inhalt schwanken nicht.
Gemessen (30.09.2026), was Swift anders macht: Je Versuch sendet Java genau
ein DISCOVER bzw. REQUEST und wartet dann bis zu einer RTT (2,5 s) auf die
Antwort; eine unpassende Antwort (etwa das Angebot des zweiten Servers) liest
es und verwirft sie. Vor dem REQUEST prüft es die angebotene Adresse per ARP
und wartet dabei eine RTT; antwortet ein anderer Rechner, lehnt es mit
DHCPDECLINE ab. Javas Server übergeht das DECLINE und hält die Adresse
weiter als Angebot fest (4 RTT). Nach dem fünften Fehlversuch (Zeitablauf)
nimmt der Client eine Link-Local-Adresse `169.254.1.x/16`: ARP-Probe, eine
RTT warten, dann ein ARP-Reply an alle.
Die RTT ist in Java nicht fest: `Verbindung.holeRTT` = 50 Kabel ×
Verzögerung je Kabel × RTT-Faktor (Startoption, 1). Beim Anlegen des
Fensters setzt das Hauptmenü den Regler auf 90 %, also 55 ms je Kabel und
2750 ms RTT — so laufen die `DhcpRun`-Referenzen; gemessen sind die
Wartezeiten 2745 bis 2751 ms. Außerdem befördert ein Java-Kabel einen Frame
nach dem anderen (`SimplexVerbindung` schläft die Verzögerung mit gesperrtem
Puffer): Sendet eine Karte zwei Frames direkt nacheinander, kommt der zweite
eine Kabellänge später.
Filius' Mitschnitt selbst ist dabei nicht ganz zuverlässig: Starten zwei
Clients zugleich, fehlt auf der Karte des zweiten dessen eigenes DISCOVER
(vier von vier Läufen von `dhcp-lan`; der Server empfängt es).
## TCP zur Laufzeit
`TcpRun` spielt auf dem Netz von `tcp-lan` (Server 192.168.0.10, Clients .20
und .30) einen festen Ablauf von Socket-Aufrufen ab, wie Filius' Anwendungen
sie machen, den Server in einem eigenen Thread. Es schreibt nach
`tcp-java-.java.json` die Frames jeder Karte (mit TCP-Flags,
Sequenz- und Bestätigungsnummer), wann jeder Aufruf begann, zurückkam oder
scheiterte, und jeden Zustand jedes Sockets (alle 2 ms abgefragt). Netzbau
und Mitschnitt teilen `DhcpRun` und `TcpRun` in `ScenarioNetwork`.
* `exchange`: Verbindung zu Port 80, `GET /`, Antwort 3000 × `x` (drei
Segmente), der Client schließt, der Server eine Sekunde nach
`CLOSE_WAIT`.
* `closedPort`: Verbindung zu Port 81, auf dem niemand lauscht.
* `lostData`: verbunden, dann gehen alle Frames verloren
(`Verbindung.setDrop`, wie der Paketverlust des Kerns), während der Client
sendet; der Verlust endet, wenn `senden` scheitert.
Gemessen (30.09.2026, 90 %, RTT 2750 ms), was Swift anders macht:
* Jeder Versuch wartet 3 RTT (8250 ms): Daten gehen dreimal hinaus (1720,
9970, 18220 ms), `senden` scheitert 8250 ms nach dem dritten.
* Scheitert `senden`, schließt Java ordentlich: FIN, ACK, FIN, ACK. Swift
setzt den Socket ohne FIN auf CLOSED.
* `TIME_WAIT` dauert 3 RTT (8250 ms; oder bis zum nächsten Segment), nicht
eine RTT.
* Die Gegenseite schließt von selbst: Kommt ein FIN, geht Javas
Socket-Thread aus `listen` in `close` und sendet sein FIN sofort
(`LAST_ACK` rund 60 ms nach dem FIN), nicht erst, wenn die Anwendung
`schliessen` aufruft.
* Der Client sendet nur **ein** SYN: Nach dem ersten Zeitablauf setzt
`connectClientMode` `closeSocket`, und die Schleife über drei Versuche
endet (`verbinden` scheitert nach 3 RTT). Der Server-Socket wiederholt sein
SYN+ACK nie.
* Startnummern aus einem Zähler über alle TCP-Sockets (1000000, 2000000, …),
wie im Kern; der freie Port ist zufällig.