Von Warntagen und Präsidenten

Es war mal wieder Bundeswarntag mit dem ersten vollständigen Probelauf von DE-Alert, der deutschen Implementierung des EU-Standards EU-Alert, der auf SMS-CB basiert. Also das, was seit mehr als 20 Jahren in USA und Japan am Start ist, und in einigen EU-Ländern wie den Niederlanden gerade 10-jähriges Jubiläum feiert.

Der Cell Broadcast hat hier vor Ort gut funktioniert, alle angeschalteten Mobiltelefone haben gewarnt. Anderswo war das wohl nicht so erfolgreich, aber bei einem Probelauf kann im Gegensatz zum Ernstfall ja auch der Fehlschlag als Erfolg gewertet werden. Die Berichte zeigen ein uneinheitliches Muster des Nichtfunktionierens, scheinbar zufällig verteilt über alle Smartphone-Modelle und Betriebssysteme und Netzbetreiber.

An einer Stelle berichtete jemand, dass bei seinem Xiaomi-Smartphone die Meldung mit “MESSAGE FROM THE PRESIDENT” betitelt war. Für mein nerdiges Ich ist das wie ein großes Schild “Bitte hier graben”. Und ich habe gegraben. Ging sogar recht schnell.

Ergebnis: Die für SMS-CB und EU-Alert relevante EU-Norm ist ETSI TS 102 900. Laut Tabelle 1 in Abschnitt 5.2.1 gibt es die höchste Prio von Warnmeldungen mit der Bezeichnung “EU-Alert Level 1”, die identisch ist mit dem Level “Presidential Alert” in CMAS (Commercial Mobile Alert System), dem US-Standard, in dem deren PWS (Public Warning System) auf SMS-CB-Basis spezifiziert ist.

Passiert also vermutlich bei älteren Android- und iOS-Versionen, die halt nur den US-Standard aus grauer Vorzeit kennen und nicht den EU-Neuaufguss davon von 2019. Wobei einige EU-Länder wie oben angesprochen auch schon seit 10 Jahren das implementiert haben, also doch eher “schlampig implementierte Software oder Übersetzung”.

Wichtiges Detail am Rande: diese höchste Dringlichkeit ist von der “Opt-Out”-Möglichkeit durch den Benutzer ausgenommen. Denn wer würde schon seinen Präsidenten ignorieren wollen. Gut, damals konnte sich vermutlich niemand Frank-Walter Steinmeier im höchsten Amt vorstellen.

Gängige Passwörter

Eine nicht uninteressante Auswertung der am häufigsten benutzten (und damit höchstwahrscheinlich eher unsicheren) Passwörter ist mir heute über den Weg gelaufen. Nachfolgend einige tiefschürfende Erkenntnisse daraus, nicht nur Passwörter betreffend.

In Deutschland auf Platz 58: “sonnenschein”. Benötigte Zeit zum Passwort-Knacken laut dieser Liste: “3 Jahren”. Zeit, bis der einfache Plural deutscher Wörter korrekt auf originär englischsprachigen Webseiten angekommen ist: voraussichtlich mehrere Jahrzehnte. Zeit, bis in allen Länder-Auswahllisten “Deutschland” alphabetisch nicht bei “G” einsortiert wird: unendlich.

OpenJDK Overflow

Früher war die Welt noch einfach. Wenn man Java wollte (und ich rede jetzt nur mal von Java SE, sonst gibt das gleich wieder eine hundertseitige Abhandlung), ging man zu java.sun.com (oder gar zu java.de), hat JDK oder JRE seiner Wahl runtergeladen, und gut war. Klar, eher nicht das JRE, weil Sun irgendwann mal anfing, irgendwelche Ad-Ware damit zu bundeln, und weil es immer Installer-gebunden war und nicht als schnödes nacktes ZIP zur Verfügung stand.

Heute, im Zeitalter von OpenJDK auf der einen Seite und Oracles wöchentlich wechselnden Lizenzbedingen auf der anderen Seite schaut man sich natürlich nach einem passenden “Stamm-Provider” eines OpenJDK-Pakets an. AdoptOpenJDK war früher mal meine erste Wahl – Auswahl zwischen HotSpot und OpenJ9, sowohl JRE als auch JDK, viele Plattformen unterstützt, sowohl x86 als auch x64. Inzwischen ist das zu “Eclipse Temurin” von “Adoptium” gemorphed, die Auswahl zwischen HotSpot und OpenJ9 scheint es nicht mehr zu geben, aber dafür gibt es bei IBM (wider besseren Wissens setze ich jetzt mal einen Link, wohl wissend, dass der in wenigen Monaten auf Nimmerwiedersehen einer Restrukturierung der IBM-Webpräsenz zum Opfer fällt) ein OpenJDK namens “IBM Semeru”, in den Geschmacksrichtungen “Certified Edition” oder “Open Edition”, für den typischen IBM-Bauchladen (System/390, AIX) aber auch für Windows/Linux/MacOS als OpenJDK-OpenJ9-Bundle. Sinn? Übersichtlichkeit? Keine Ahnung. Derweil notiere ich: für die Top-IBM-Plattformen S/390 und AIX gibt es als neueste Version Java 11. Auf IBM-Plattformen meint “long term” eben meistens die lange Zeit, bis aktuelle Versionen zur Verfügung stehen. Meine letzte gute Erfahrung mit IBM JDKs war mit Java 1.3, als auf einem linux-basierten ThinClient mit fast nacktem XServer das IBM-Java deutlich besser mit TrueType-Fonts umgehen konnte als die Sun-Konkurrenz. Lange her. Ansonsten zeichnete sich die IBM-Variante oftmals durch höheren Speicherverbrauch aus (ich erinnere mich an die Class-Sharing-Lösung in JDK 1.5, die zwar relativ gesehen pro zusätzlicher JVM-Instanz eine gute Menge Speicher sparte, aber absolut gesehen gegenüber der Sun-Lösung leider deutlich mehr verbrauchte) und dazu kamen noch abstruse Bugs im JIT-Bereich sowie irgendein dubioses Encoding-Problem im XML-Bereich, das mir den Schlaf raubte.

Ich will jetzt nicht alle OpenJDK-Distributionen aufzählen, die Menge ist inzwischen unüberschaubar, Anbieter kommen und gehen, zeichnen sich durch unvollständige Unterstützung diverser Plattformen aus, haben undurchschaubare Eigeninteressen…unterm Strich habe ich mich jetzt auf Bell Software eingeschossen, die bieten eine OpenJDK-Distribution namens “Liberica JDK” zum Download an. Besonderheit aus meiner Sicht: alle Versionen bis zurück zu Java 8, alle für mich relevante Plattformen, sowohl JRE als auch JDK, sowohl x86 als auch x64, und als besonderen Service auch noch “Full JRE” und “Full JDK”, ein Bundle bestehend aus Java SE und OpenJFX. Und dazu noch pre-built docker images, z.B. in einer Minimalversion mit Alpine Linux mit musl-Standard-Lib. Seit Längerem verwende ich praktisch nur noch die diversen Bell-Distributionen, alles funktioniert prächtig – ich habe aufgehört, nach besseren Alternativen zu suchen.

Eine interessante, aus meiner Sicht aber noch eher experimentelle Variante: OpenJDK in der GraalVM Community Edition. Mit dem Graal-JIT, der je nach Workload durchaus Vorteile bringen kann. Derzeit downloadbar als Version 22.1 in den Geschmacksrichtungen Java 11 und Java 17. Quasi der Außenseitertipp unter den OpenJDK-Distributionen.

Zum Schluss noch ein typisches “wann ist mir denn das entgangen” beim Abprüfen, ob noch alle Anbieter an die ich mich erinnere im Rennen sind: was ist eigentlich JITServer? Das habe ich gerade zum ersten Mal gehört bzw. gelesen. Es gibt aber schon seriöse Infos zu diesem Thema von Januar 2020, mit Andeutungen eines existierenden Prototyps namens “JIT-as-a-Service” schon Mitte 2019. Auch so eine IBM-OpenJ9-voll-das-fancy-Cloud-Zeugs-Geschichte. Dieses Java-Ökosystem kann einen in den Wahnsinn treiben, wenn man da auf dem Laufenden sein und bleiben will. Nächstes Mal zu diesem Thema: warum fängt man mit “Project Leyden” an, wenn man schon Graal hat? Die Antwort wird wohl eine ähnliche sein wie bei “warum Java Modules, wenn es schon OSGi gibt”. Es kann was Gutes draus werden, aber man weiß es noch nicht, und es ist ein Haufen Arbeit auf einer ganz langen Zeitschiene. Und entlang dieser Zeitschiene liegen jede Menge Leichen aus früheren gut gemeinten Ansätzen.

Fortgeschrittene 2-Faktor-Authentifizierung

Neulich im Dunstkreis von Zahlungsdienstleistungen. Ich wollte bei einem Vertrag meine Mobilfunknummer ändern. Übers Webinterface eingeloggt und die Nummer geändert. Ein Popup informiert mich darüber, dass das leider gar nicht so einfach möglich sei, denn die Mobilfunknummer werde mittels SMS-TAN als zweiter Faktor für Authentifizierung genutzt. Ich hätte die Wahl zwischen Brief-TAN – dann müsse ich aber beim Service-Center anrufen und dies veranlassen – oder könnte die App nutzen, dort die Mobilfunknummer ändern, und bekäme dann eine SMS-TAN.

So weit, so plausibel – natürlich ging ich davon aus, dass ich dazu dann zwei Handys (oder in Zeiten von Dual-SIM: zwei Rufnummern) benötige, einmal die alte hinterlegte Nummer und einmal die neu zu hinterlegende Nummer. Die Frage “warum kann ich nicht einfach mit der alten Nummer und einer SMS-TAN die neue Nummer authentifizieren” stellte ich mir, beantwortete sie mir aber mit dem naheliegenden “die Software kann das vermutlich nicht, weil einer vergessen hat, diesen Fall zu spezifizieren”.

Also die App auf dem neuen Handy installiert, über die App eingeloggt (gleiche Credentials wie das Webinterface), Mobilfunknummer geändert, und die SMS-TAN auf die neue (!) Mobilfunknummer bekommen. Also auf dasselbe Gerät, auf der die App läuft.

Freunde: so funktioniert das nicht mit der 2-Faktor-Authentifizierung. Das ist einfach nur Bullshit und Pseudo-Security.

 

Windows-Fehlermeldung des Monats

Neulich am Windows 10-Rechner meiner Wahl. Daten werden auf einen USB-Stick kopiert zwecks Transfer zu einem nicht netzwerkfähigen Gerät. Zielformat FAT32. Kommt bei einer Datei mittendrin folgende Fehlermeldung:

“Fehler x80240022: Im Laufwerk befindet sich eine falsche Diskette. Legen Sie %2 (Datenträgernummer: %3) in Laufwerk %1 ein.”

Nur echt mit nicht aufgelösten Platzhaltern. Das einzig Gute, was man über diese Fehlermeldung sagen kann: sie ist dermaßen absurd neben der Spur, dass man wenigstens nicht in Versuchung kommt, einer falschen Fährte zu folgen.

Gruseliger als diese Fehlermeldung – und da liegt die Latte wahrlich hoch – ist nur, wenn man nach der Fehlernummer googelt und diverse Artikel in der Microsoft-Knowledge-Base darüber findet. Mein Favorit: wenn der Fehler unter Windows 2000 auftaucht, wenn man den Microsoft Defender updated, muss man die Root Certificates erneuern oder installieren…

Die vergessene Programmiersprache

Schon früher in diesem Blog habe ich mich als Ada-Anhänger geoutet. Ich habe in Ada eine komplette CD-/DVD-/BluRay-Brenner-Software geschrieben, weiß also so ungefähr von was ich rede. Die weitaus meisten Zeilen Code habe ich allerdings in anderen Programmiersprachen, vornehmlich in Java, verbrochen.

Schon als ich damals 1996 nach vielen Jahren Ada meine ersten Gehversuche mit Java (Version 1.0.2, die Programmier-Opas erinnern sich) machte, rieb ich mir verwundert die Augen. Wie kann man Mitte der 90er, mit so vielen Vorarbeiten wie Algol und Smalltalk und Pascal und Modula und Ada eine derart rudimentäre, vor allem bei den Basisdatentypen so schwach aufgestellte Sprache wie Java auf den Markt werfen? Es wirkte wie eine minimale OO-Erweiterung wie es sich C-Fans ausmalen würden. Keine Generics, keine Enums, keine funktionalen Elemente, kein vernünftiges Scoping (Methode-in-Methode), kein Operator Overloading, keine “unsigned”-Typen, ein sehr schwaches Konzept der Nebenläufigkeit…vom Sprachstandpunkt aus wirkte es wie ein Zurück-in-die-70er. C, eine Prise Objektorientierung, der Rest des Gehirnschmalzes steckte dann in der Standardbibliothek und der virtuellen Maschine nebst der Applet-Idee.

Seither ist viel Zeit vergangen. Jede Menge Programmiersprachen kamen und die meisten gingen auch wieder. Im Bereich systemnahe Programmierung scheint derzeit Rust immer noch der Hype der Stunde zu sein. Und jeder Ada-Kenner fragt sich: was soll denn an Rust eigentlich so besonders sein? Wäre da Ada nicht von Anfang an und seit 1980 die bessere Wahl gewesen? Um Salz in die Ada-Wunde zu streuen, arbeiten sich die Rust-Evangelisten ja auch immer an C als Benchmark ab, anstatt mal über den Tellerrand zu schauen.

Eine Frage kann ich jedenfalls beantworten: warum Ada keine weitere Verbreitung geschafft hat. Das liegt schlicht am (fehlenden) Ökosystem. Vor GNAT gab es keinen bezahlbaren, qualitativ hochwertigen Compiler. Und seit GNAT gibt es leider immer noch keine problemlos und überall einsetzbare Toolchain. Die von AdaCore gepflegten Pakete haben schon auf der Windows-Plattform ihre Stabilitätsproblemchen wenn man auf die “Libre”-Schiene setzt. Andere CPUs als x86/x64 werden sowieso nur sparsam unterstützt. Jahrelang musste man, wenn man für ARM compilieren wollte, ganz bestimmte FSF-GCC-Versionen einsetzen und extreme Vorsicht walten lassen – meist kam man nicht umhin, GNAT selbst zu bootstrappen. Dazu das IDE-Problem: egal ob Plugins für die großen IDEs wie Eclipse, VisualStudio oder NetBeans, oder Standalone-Pakete wie GPS: die Qualität war bestenfalls durchwachsen, teilweise katastrophal schlecht oder in den Anfängen stecken geblieben (Projekt Hibachi). Und manchmal auch nur ein klassisches “one-off” wie GNATbench im Libre-Bereich. Geholfen hat sicher auch nicht AdaCore’s Taktik, die Libre-Toolchain GPL-only zu machen. So mussten interessierte Entwickler, die nicht an die GPL gebunden sein wollten, auf die FSF-GCC-Variante ausweichen, mit den ganzen zusätzlichen Reibungsverlusten. Oder die one-offs wie “GNAT for JVM” oder “GNAT for .NET” – sowas sorgt einfach nicht für das Gefühl von Stabilität und Verlässlichkeit, eigentlich Stärken von Ada.

Dadurch ergab sich auch eine große Distanz zwischen Hobby-Programmierern und der Profi-Welt: Ada ist ja sehr verbreitet in gewissen Industriebereichen, vor allem Militär und Luftfahrt. Dort dominieren die extrem teuren Ada-Toolchains und IDEs mit ihren zig Zertifizierungen und Gedöns, die Bemühungen z.B. von Aonix mit ObjectAda im Zeitalter von Windows 95 und NT waren auch eher kurzlebiger Natur. Und so leben die Profis in ihrer Welt, und die Hobby-Programmierer verwenden weit überwiegend lieber eine leichter zugängliche Programmiersprache.

Nimmt man dann noch die Problematik “Drittbibliotheken” dazu, die in der Ada-Welt eher sparsam vorhanden waren, hat man eben einfach kein konkurrenzfähiges Ökosystem, sondern Gefrickel. Untauglich für Neueinsteiger, mühsam für interessierte Hobbyisten, frustrierend selbst für Profis. Immerhin gibt es inzwischen Bemühungen, einen Ada-spezifischen Paket- und Library-Manager zu entwickeln mit dem schönen Namen Alire. Noch Beta. Too little, too late. Auch die Bemühungen um Ada-Unterstützung in Visual Studio Code unterzubringen sind respektabel, stecken aber noch in den Kinderschuhen und sind mit dem Stand anderer Sprachunterstützung wie beispielsweise für Python noch lange nicht vergleichbar.

Will man heute eine Programmiersprache in den breiten Einsatz bringen, reicht eben ein Compiler und ein bisserl Toolchain drumrum bei weitem nicht aus. Man braucht über viele Systeme verfügbare, stabile Toolchains. Man braucht IDEs. Man braucht Bibliotheken. Beispielanwendungen. Dokumentation. Tutorials. Viele Seiten, die sich mit Ada beschäftigen, scheinen direkt dem Internet der 90er entsprungen zu sein (hier z.B. eine ZIP-Bibliothek). Damit kann man bei den Entwicklern von heute einfach nicht mehr in der Breite landen und bleibt im Exotendasein stecken.

Es ist unendlich schade um das Potenzial, das in Ada steckt. Aber meine Prognose lautet: das wird nix mehr. Ada wird zurecht vergessen bleiben.

Gedanken zum aktuellen Log4j-Problem

Viele haben sich schon zu unterschiedlichen Aspekten des aktuell in der Sonne der Aufmerksamkeit stehenden Log4j-Remote-Code-Execution-Problems-durch-clever-formulierten-User-Input-der-geloggt-wird geäußert. Mir fällt dabei vor allem auf, dass die meisten Kommentatoren zwar kleine Teilaspekte des zugrundeliegenden Problems meist korrekt beschreiben, es aber an abwägendem sowohl-als-auch eher mangelt. Denn m.E. ist diese Misere keins von den Problemen, die eine einfache Lösung haben.

Ich kann mit vielen der bereits von Anderen genannten Teilaspekte mitgehen. Ja, in Java kann man z.B. durch fortgeschrittene Featuritis und Libraritis und Frameworkitis sehr komplexe Lösungen bauen, die keiner mehr durchschaut. Aber das geht in praktisch jeder Programmiersprache, außer in denen, die schon unhandlich werden, sobald man versucht mäßig komplexe Probleme zu lösen. Hier ein Java-Spezifikum zu sehen ist komplett absurd. Ja, 3rd-party-Abhängigkeiten die man nicht durchverstanden hat sind potenziell gefährlich. Aber selbstgebaute Lösungen sind ebenfalls gefährlich. Ja, der Zeitdruck und die inadäquate Bezahlung in vielen Softwareprojekten ist eine der vielen Problemursachen. Aber auch ohne Zeitdruck und mit unendlichen Mitteln können Katastrophen programmiert werden. Ja, man sollte danach streben, möglichst einfache Lösungen zu bauen. Aber einfache Lösungen, die gleichzeitig problemadäquat sind, fallen auch nicht vom Himmel und brauchen Zeit und Geld – Vereinfachung ohne es zu einfach zu machen ist eine hohe Kunst.

Beim gerade aktuellen Problem kann man es sich einfach machen und sagen “was kann an Logging schon so schwer sein”. Ja dann nehmt doch einfach System.out.println, wenn das ausreichend ist. Kurze Zeit später werdet ihr feststellen, dass es ganz nützlich wäre, mehrere Logkanäle zu bedienen. Den Loglevel von außen konfigurierbar zu machen. Beim Schreiben von Logfiles jedes einzelne File nicht zu groß werden zu lassen. Auf Effizienz beim Loggen achten, also am besten asynchron arbeiten. Und die Log-Datenbank xy anbinden. Für das Loganalysetool der Wahl den Timestamp der Logmeldung parametrierbar zu machen. Ich glaube nicht, dass eine signifikante Anzahl der Log4j-Features “einfach so” eingebaut wurden, da steckt schon jeweils ein valider Usecase dahinter. Und es hat seinen Grund, warum es nicht nur Log4j gibt, sondern auch JUL oder Logback oder tinylog – oder der gute alte IBM LogManager, falls den noch jemand kennt. Wer die Features eines Log4j nicht will oder braucht, versteckt sich hinter slf4j und überlässt dem Kunden die Auswahl der finalen Logimplementierung – aber öffnet gleichzeitig einen interessanten neuen Angriffsvektor, wenn Schadcode beim Deployment als slf4j-implementierendes Jar eingeschleust wird. Und wenn ich dann nach aufwändiger Analyse zum Schluss komme, dass Logback alle meine Wünsche erfüllt, kommt morgen eine neue Anforderung um die Ecke, die nur durch Log4j abgebildet werden kann – was dann? Anforderung ablehnen? Lib wechseln? Von vorne anfangen?

Man soll sich keinen Illusionen hingeben. Was bei Log4j passiert ist, kann mit jeder Programmiersprache, auf jedem Betriebssystem, ja sogar auf jeder CPU passieren. Spectre konnte über JavaScript im Browser ausgenutzt werden – noch so viele Schichten zwischen Quellcode und Ausführung haben uns nicht vor den Auswirkungen eines CPU-Bugs geschützt. Schon im guten alten Z80 gab es “illegale” Opcodes, die trotzdem etwas Vernünftiges gemacht haben, als Seiteneffekt der Implementierung. Nur 8500 Transistoren, und trotzdem ein solcher “Bug” (oder “Feature”, je nachdem wen man fragt). Der aktuelle Apple M1 Max besteht aus 57 Milliarden Transistoren. Natürlich nicht direkt vergleichbar, weil jede Menge Cache damit realisiert ist, aber auch durch Caches können ja interessante Effekte entstehen (Stichwort Seitenkanalangriff). Jedenfalls ein geeignetes Beispiel für die immens gewachsene Komplexität auf allen beteiligten Ebenen.

Ich bin auch ein Fan davon, mit möglichst wenigen Abhängigkeiten in meinen Java-Anwendungen auszukommen. Aber diese Strategie trägt eben auch nicht unendlich weit. Beispiel: wenn Persistenzierung in einer Datenbank gefragt ist, kann man von Hand JDBC machen oder Hibernate verwenden oder JPA oder JDO oder was auch immer das Framework der Wahl so mitbringt. Oder man speichert seine Daten in einer simplen Datei. Oder doch über BerkeleyDB oder SQLite? Wenn die Anforderung lautet, einen Webservice zu schreiben, beginnt man dann mit seinem eigenen Server und seiner eigenen Security-Schicht, oder nimmt man doch was von der Stange und vertraut darauf, dass die Profis es entwickelt haben? Ist eine Bibliothek, die hundert unnütze Funktionen zusätzlich zu den für mich nützlichen zehn bietet, denn notwendigerweise die falsche Wahl, wenn die Konkurrenzbibliothek mit nur zwanzig unnützen Funktionen z.B. closed source ist oder viel weniger Nutzer hat oder eine viel kleinere Community? Oder alles drei? Ist es wirklich sinnvoll, sich auf den Featureumfang der Java-Plattform zu beschränken und sklavisch auf 3rd-party-Libs zu verzichten – ist denn sichergestellt, dass die “Bordmittel” wirklich von höherer Qualität sind als Drittbibliotheken?

Auf Basis von Post-hoc-Erkenntnissen klug daher schwätzen (“hättet ihr mal nicht Log4j verwendet” – ersetze Log4j durch “Intel-CPU” oder “OpenSSL” oder “PHP” oder “Linux” oder “Docker” oder “IIS” oder “Applets”) kann jeder. Aber was ist die “richtige Strategie” im Angesicht des Ungewissen, wenn man vor einer erheblichen zu implementierenden Komplexität steht, die nicht mal schnell ein Profi-Entwickler im Alleingang und minimalen Abhängigkeiten hindengelt (und dieser Profi dann auch dauerhaft für die Wartung zur Verfügung stehen muss)? Es wird letztlich immer eine Abwägungsfrage bleiben. Tritt kein Problem auf, war die Abwägung tendenziell richtig – oder man hat einfach nur Glück gehabt.

Vermutlich ist das die einzige vielversprechend Strategie. Einfach Glück haben.

Da fällt mir ein: schon 2015 habe ich unter der schönen Wortschöpfung “Abstraktionskaskade” diese Problematik beleuchtet. Und schon damals war ich nicht der erste.

Goodbye, Schily – ein Nachruf

Die traurige Nachricht von Jörg Schillings viel zu frühem Tod hat nun auch mich erreicht. “Schily”, so sein Kürzel beispielsweise im Heise-Forum oder generell bei den Releases seiner zahlreichen nützlichen Tools, starb in viel zu jungen Jahren an Krebs.

Ich hatte nie persönlichen Kontakt mit ihm, obwohl wir beim Thema “Brennersoftware” ja größere Überschneidungen hatten. Sein cdrecord ist nach wie vor der Gold-Standard unter den freien Brennprogrammen. Ja, ich hatte sogar in der Anfangszeit mal einen Blick in die Sourcen geworfen, um herauszufinden, wie man diese Yamaha CDR100/102-Brenner und den Teac CD-R50S zum Brennen überreden könnte. Aus potenziellen Lizenzunverträglichkeitsgründen habe ich das aber mit Beginn der CDBurn-Entwicklung fürderhin unterlassen – nicht etwa nur, weil das Lesen von C-Code irgendwann das Hirn verschwurbelt. Ich muss zugeben, ich war immer ein wenig neidisch darauf, dass er es geschafft hatte, dass diverse Firmen wie Sanyo, LG, Sony, Plextor und Yamaha ihm kostenlos und dauerhaft Laufwerke zu Test- und Entwicklungszwecken zur Verfügung zu stellten. Was aber nur gerecht war, denn ein portierbares Multi-OS-Brenntool ist im großen Spiel der Dinge ganz sicher von größerem Interesse als eine kommerzielle Implementierung auf einem Randgruppenbetriebssystem wie RISC OS.

Irgendwann Ende der 90er oder Anfang der 00er Jahre hatte ich mal eine kurze Unterredung mit einem GPL-/Linux-Fan, der mich überreden wollte, doch CDBurn unter der GPL und für Linux zu veröffentlichen. Auf meine überraschte Nachfrage, warum das im Angesicht von cdrecord eine gute Idee sein sollte, erfuhr ich zum ersten Mal von der eher GPL-ablehnenden Haltung von Schily und seiner auch sonst meinungsstarken und damit wenig kompromissbereiten Persönlichkeit. Und nach näherer Beschäftigung mit seinen Argumenten konnte ich Schilys Standpunkten letztlich zustimmen, ebenso den meisten seiner Einlassungen in diversen Foren zu diesem Thema. Auch wenn ich es nie zu seinem Grad der GPL-Ablehnung geschafft habe – meine kritische Einschätzung der GPL und der grundsätzlichen Schwierigkeiten bei der Interpretation dessen, was diese Lizenz in welchem Szenario nun zulässt oder nicht, ist für immer geblieben.

Immer streitbar und trotzdem produktiv. Ich werde Schily in guter Erinnerung behalten. Session closed.

Zwischenprojekt: KVMPortSwitcher

Meine Entwicklungsstrategie für private Projekte folgt meist einer Stack-Strategie: je älter die Idee, desto tiefer vergraben, ständig kommt neues Zeugs oben drauf, und in seltenen Fällen findet das jüngste Projekt direkt zur Reife. Und so geschah es mit dem jüngsten Spross meines bunten Straußes an Java-Projekten: KVMPortSwitcher. Wer nur den technischen Sermon lesen und den Code sehen will, darf direkt zum Projekt auf GitHub abbiegen.

Um was geht es? Seit einiger Zeit besitze ich einen “TESmart 16-port HDMI KVM Switch” (das exakte Modell gibt es offenbar nicht mehr, aber hier ist der Nachfolger zu finden). TESmart ist der einzige mir bekannte Hersteller, der KVM-Switches mit 8 Ports und mehr zu einigermaßen bezahlbaren Preisen anbietet und trotzdem problemlos funktioniert – meine Anforderungen damals waren “4K, HDMI, 8 Ports oder mehr, keine Spezialkabel”. Dadurch schrumpfte die Zahl der zur Auswahl stehenden Geräte nach ausführlicher Recherche auf “1”.

Nun gehört alles mit 8 Ports und mehr offenbar üblicherweise der Profi-Fraktion an, und so hat dieser Switch zusätzlich einen Ethernet-Anschluss, mit dem man ihn ins LAN integrieren kann, um ihn so über IP steuern zu können. Warum würde man das wollen? Zum einen zwecks Konfiguration des Geräts. Aber vor allem zur Steuerung des ausgewählten Ports. Denn die anderen Wege, den Port zu wechseln, sind mit verschiedenen Problemen und Nicklichkeiten verbunden, die einem ganz schön auf die Nerven gehen können.

Man kann die Knöpfe an der Front des KVM-Switches verwenden. Prinzipiell keine schlechte Idee, aber beim 16-Port-Modell braucht man dabei für die Geräte 10-16 zwei Tastendrücke. Oder sogar drei, denn wenn der Display-Timeout zugeschlagen hat, dient der erste Tastendruck gar nicht dem Umschalten, sondern dem Aktivieren. Also zwei oder drei Tastendrücke, und man muss natürlich die Hand von der Tastatur nehmen.

Wie wäre es mit der IR-Fernbedienung? Anders, aber nicht besser. Wenn man sie denn mal gefunden hat (vorsichtshalber steht nur “Remote Control” drauf, damit man sie ja nicht klar zuordnen kann), kann man zwar die Ports 2-9 durch einen Tastendruck auswählen, die Ports 1 und 10-16 hingegen nicht – was besonders bei “1” sehr dumm ist, da drückt man auf “1” und wundert sich, dass nix passiert, bis dem Gerät klar wird, dass keine Eingabe mehr erfolgt. Wer jetzt denkt, er könne durch zwei Tastendrücke “0” und “1” das Gerät überlisten, sieht sich getäuscht. auch “1” gefolgt von “OK” hilft nicht. Immerhin braucht es den “Aktivierungstastendruck” wie bei den Front-Panel-Buttons nicht.

Wie wäre es mit der eingebauten Möglichkeit, über die Tastatur den Port zu wechseln? Im Prinzip eine gute Idee, aber: es funktioniert nur mit den im Gerät hartcodierten Shortcuts, und die sind eher komisch, aber natürlich so gewählt, dass sie möglichst nicht mit anderen gängigen Tastatursteuerungen kollidieren: zwei Mal Scroll Lock (aka “Rollen”), dann 1-n. Man erkennt direkt das Problem für die 16-Port-Variante: bei Rollen-Rollen-1 gibt es die von der Fernbedienung bekannte Gedenksekunde, bis klar ist, dass man wirklich “1” meint und nicht “10” oder “13” und nur langsam tippt. Und noch ein Haken: das funktioniert natürlich nur, wenn die Tastatur im dafür vorgesehenen spezifischen USB-Port steckt, der diese Logik enthält – dieser Port ist aber nicht kompatibel mit allen Tastaturen dieser Welt, und so muss man manchmal auf den “nackten” USB-Port ausweichen. Und schon ist es Essig mit der Hotkey-Funktionalität.

Insgesamt gibt es also ausreichend Gründe, alternative Möglichkeiten zum Umschalten der Ports zu suchen. Eine davon bietet TESmart selbst an mit einem kleinen Tool, das eine IP-Verbindung aufbauen kann, dadurch den aktiven Port feststellt und dann per Mausclick das Umschalten erlaubt. Schön, aber natürlich eher keine Verbesserung zu den anderen drei Varianten. Tatsächlich dauert es doch recht lange, bis das Tool endlich den “Connect” hergestellt hat – keine Ahnung, warum. Mehrere Sekunden jedenfalls.

Nun muss man TESmart zugutehalten, dass sie das Protokoll über die IP-Schnittstelle zur Ansteuerung der Funktionalität öffentlich dokumentiert haben – leider muss man solche Dinge ja positiv vermerken, da es sich nicht um eine Selbstverständlichkeit handelt. Jedenfalls ist es denkbar einfach: IP-Verbindung herstellen, ein paar Bytes senden, fertig ist die Laube.

Ich hatte also so ein Projekt schon länger im Hinterkopf – meine Brot-und-Butter-Implementierungssprache Java schien da aber ungeeignet, weil man dort keine systemweiten Keyboard-Shortcuts beim Betriebssystem registrieren kann, und eine weitere grafische Oberfläche, wo man per Click den KVM-Switch bedienen kann, wollte ich nun auch nicht bauen. Zufällig bin ich bei meinen Streifzügen durch das Internet dann aber über eine Bibliothek namens JNativeHook gestolpert, die für alle gängigen Betriebssysteme und CPU-Architekturen native code zusammen mit etwas Java-Glue-Code zur Verfügung stellt, um solche globalen Hotkey-Listener zu implementieren. Kurz getestet, tut, also frisch ans Werk.

Der Code an sich und die UI war natürlich trivial. Kurz noch ein TrayIcon dazugeschraubt (denn für eine Anwendung, die normalerweise im Hintergrund läuft, ist es immer geschickt, noch einen Haken zur Kontrolle derselben zu haben), ausprobiert. Funktionierte – meistens. Denn es gab ein interessantes Phänomen, dass der NativeKey-Event-Dispatcher manchmal, wenn der Port umgeschaltet wurde, munter weiter denselben Event wieder und wieder feuerte. Ich bin dem noch nicht auf den Grund gegangen, aber habe einen klassischen Workaround gefunden: asynchrone Verarbeitung, der Listener löst nicht direkt den Port-Switch aus, sondern scheduled einen extra Thread, der kurz wartet und dann erst umschaltet. Das scheint das Problem zuverlässig gelöst zu haben.

Wer bis hierhin durchgehalten hat, darf jetzt zum GitHub-Projekt abbiegen. Es gibt auch ein runnable jar nebst inkludiertem jnativehook-Jar als Release 0.1.0 zum Download bei GitHub, Java 8 wird zur Ausführung benötigt. Wer also zufällig einen TESmart HDMI-KVM-Switch sein Eigen nennt und ihn in sein Heimnetz integriert (hat), kann direkt loslegen.

Auf der GitHub-Projektseite kann man auch nachlesen, was noch die TODOs sind. Konfigurierbare Port-Namen (bei 16 Ports kann man schon mal den Überblick verlieren), konfigurierbare Keyboard-Shortcuts, i18n (bis jetzt ist alles hartcodiert englisch). Und vielleicht auch noch für die old-school-Generation die Steuermöglichkeit über die serielle Schnittstelle des TESmart-KVM-Switches?

Java. Swing. Dem Wahnsinn nahe.

Seit Swing 1.0.2, als das Package noch com.sun.java.swing hieß und Java 1.1 gerade der heiße Scheiß war, programmiere ich UIs mit Java und Swing. Es ist so eine Art Hassliebe. Nach wie vor halte ich es für den einfachsten und besten Weg, Cross-Platform-Anwendungen zu schreiben, die mehr oder weniger problemlos unter Windows und Linux und MacOS funktionieren. Und an sonnigen Tagen auch unter AIX und Solaris.

Aber manchmal…manchmal…graue Haare, Stirnrunzeln beim Debugging, dem Wahnsinn nahe. Ich baue gerade an einem kleinen Projekt dessen tieferer Sinn oder Unsinn hier nichts zur Sache tun soll. Jedenfalls wird dort als Anzeigekomponente die allseits beliebe JEditorPane mit HTML-Inhalt verwendet. Das ist von jeher etwas kitzelig weil ein etwas merkwürdiges HTML-3.2-Sub-und-Superset-mit-etwas-CSS unterstützt wird, und man sollte sehr genau wissen welches HTML man reinsteckt und was unfallfrei gerendert werden kann. Immer spannend war das Thema Grafiken. Im Prinzip sollte es ja ein img-Tag mit passender URL dahinter tun. Blabla.class.getResource(“/schnickschnack.png”) liefert ja schon eine lokale File-URL zurück, was kann da schon schiefgehen.

Eine ganze Menge. Und was ist, wenn man gar keine Datei hat, sondern ein beliebiges Image in-memory? Weil das so ist, haben schlaue Menschen einen Weg ersonnen, wie man der JEditorPane – oder besser gesagt dem HTMLDocument, das über den HTMLEditorKit beim ContentType “text/html” bekanntlich entsteht – ein beliebiges Image unterjubeln kann, indem man es in einen “imageCache” unter der im img-Tag hinterlegten URL steckt.

Hier ein Link zu Sourcecode, der illustriert, wie das prinzipiell geht. Sehr heikel, weil es sich auf ein Implementierungsdetail verlässt, aber Swing ist bekanntlich tot…äh…stabil, und so wird das wohl auch in Zukunft problemlos funktionieren. Dachte ich.

Wenn man nun aber obigen Sourcecode mal in der IDE der Wahl compiliert und zur Ausführung bringt, erlebt man leider eine Überraschung: mit Java 7 funktioniert das noch problemlos, mit Java 8 und neuer aber nicht. Ein beherztes Debugging an der richtigen Stelle – javax.swing.text.html.ImageView.loadImage – brachte keinen Aufschluss – das Image wird korrekt aus dem Cache geholt, seine Größe wird inspiziert, es wird layoutet, und dann…wird nichts angezeigt.

Ich will es nicht zu spannend machen, denn es gibt einen Workaround. Auch wenn ich gerade überhaupt nicht verstehe, warum der was ändert, weil genau das, was er eventuell ändern könnte, im Debugging nicht nach dem Problem ausgesehen hat.

Wenn man die Zeile

"<img src=\""+localImageSrc+"\">\n" +

ändert in

"<img src=\""+localImageSrc+"\" width=100 height=50>\n" +

dann funktioniert es plötzlich prima von Java 7 bis einschließlich Java 16.

Wer auch immer das im Detail analysieren will – Freiwillige vor.