JavaBeans, XML und Java Version 17 - oh, my!

vorhergehende Artikel in: Java Komponenten
28.06.2023

Ich habe neulich eine meiner alten Anwendungen hervorgeholt und zwei sehr alte Tickets schließen wollen - das eine zwei und das andere sogr drei Jahre alt. Da niemand außer mir darauf wartete, war dieses hohe Alter der Tickets nicht weiter schlimm. Das Schlimme kam aus einer völlig unerwarteten Richtung...

Java hat bereits seit Ewigkeiten Unterstützung eingebaut, um beliebige Objektgraphen nach XML zu serialisieren und aus XML wieder zurück zu deserialisieren. Handelt es sich bei den Objekten im Graph um JavaBeans (vollständiger innerer Zustand durch Getter/Setter für Properties beschrieben und parameterloser public Konstruktor in einer public Klasse) muss man michts weiter tun - einfach die Wurzel des Objektgraphen an den XMLEncoder übergeben und fertig.

Da die Welt um uns herum nicht so einfach gestrickt ist, sind nicht alle Objekte JavaBeans - daher existieren PersistenceDelegate, die es erlauben, die Serialisierung so zu beschreiben, dass auch Klassen, die beispielsweise keinen parameterlosen Konstruktor aufweisen trotzdem vom XMLEncoder serialisiert werden können. Beim Deserialisieren ist kein gesonderter Aufwand nötig - XMLEncoder und PersistenceDelegate, zusammen erzeugen immer ein solches XML, das vom XMLDecoder sofort verstanden werden kann.

Bis hierher klingt die Theorie recht schön. Und dann kam Java17 und das Modulsystem machte alles kaputt...

Eins meiner Tickets besagte, dass ich dem Konfigurationsobjekt eine weitere Property vom Typ java.io.File hinzufügen solle. Bisher enthielt dieses bereits vier davon, wobei diese aber jeweils Verzeichnisse waren - keine Dateien. Diese neue Property sollte aber eine Datei enthalten. Für diese Konfiguration existierte eine graphische Nutzeroberfläche, sodass die Verzeichnisse mit einem eigens konfigurierten JFileChooser ausgewählt wurden, der nur die Auswahl von Verzeichnissen zuließ. Dies funktionierte bisher jahrelang hervorragend. Nachdem ich die neue Property hinzugefügt hatte und in der GUI dafür gesorgt hatte, dass es möglich war, diese auch hier mittels eines JFileChooser zu setzen (hier konnte man natürlich auch normale Dateien auswählen) stellte ich fest, dass die XML-Serialisierung nicht mehr funktionierte: Der Code dafür bemängelte, dass er nicht wüsste, wie er Instanzen der Klasse ShellFolder serialisiern solle.

Es stellt sich heraus, dass der JFileChooser, wenn eine Datei ausgewählt wird und anschließend getSelectedFile() aufgerufen wird keine Instanz der Klasse File, sondern eine der Klasse ShellFolder aus dem Package sun.awt.shell zurückliefert. Diese Klasse ist eine mittelbare Kindklasse von File.

Also versuchte ich, für den ShellFolder ein geeignetes PersistenceDelegate zu erstellen - das wiederum schlug mit der Fehlermeldung fehl, dass java: package sun.awt.shell is not visible (package sun.awt.shell is declared in module java.desktop, which does not export it) - ganz großes Kino! Ich behalf mir zunächst damit, dass ich sämtliche Setter in meinen Beans wie folgt änderte, wenn der Typ der zugehörigen Property File war:

@Property
public void setFileProp(File fileProp)
{
	File old = getFileProp();
	this.fileProp = new java.io.File(fileProp.toURI());
	send("fileProp", old, getFileProp());
}

und überall, wo ich bisher einen JFileChooser einsetzte, nunmehr folgende davon abgeleitete Klasse verwende:

import javax.swing.filechooser.FileSystemView;
import java.io.File;

public class FileChooser extends javax.swing.JFileChooser { public FileChooser() { super(); }

public FileChooser(String currentDirectoryPath) { super(currentDirectoryPath); }

public FileChooser(File currentDirectory) { super(currentDirectory); }

public FileChooser(FileSystemView fsv) { super(fsv); }

public FileChooser(File currentDirectory, FileSystemView fsv) { super(currentDirectory, fsv); }

public FileChooser(String currentDirectoryPath, FileSystemView fsv) { super(currentDirectoryPath, fsv); }

@Override public File getSelectedFile() { java.io.File rv=super.getSelectedFile(); if(rv!=null) if(java.io.File.class!=rv.getClass()) rv=new java.io.File(rv.toURI()); return rv; } }

Damit, dachte ich, sei alles ausgestanden - doch ich wurde eines Besseren belehrt: die sQLshell serialisiert ihre Konfiguratin ebenfalls mithilfe des XMLEncoder und hier sieht man die Fehlermeldung java.lang.NoSuchMethodException: =ZoneInfo.getRawOffset(); - auch hier versuchte ich nun, einen PersistenceDelegate zu erstellen - und wurde mit der ähnlichen Fehlermeldung konfrontiert: java: package sun.awt.shell is not visible (package sun.awt.shell is declared in module java.desktop, which does not export it) - auch hier also spuckte mich das Modulsystem von Java (17) an.

Die "Lösung" für all das ist das gute alte --add-exports. Ich halte das für keine gute Lösung... Außerdem habe ich sie hier bei mir noch nicht zum Funktionieren gebracht. Also wieder mal vielen Dank für nichts. Früher, in der guten alten Zeit hätte ich wenigstens noch Larry Ellison verfluchen können aber selbst diese Genugtuung wurde mir genommen...

Artikel, die hierher verlinken

XML-Serialisierung und Java-Generics

Es ist verwunderlich, dass ich in den letzten Wochen mehrfach über Probleme mit der Serialisierung von Objektgraphen in XML in Java stolpere - aber es ist passiert...

Alle Artikel rss Wochenübersicht Monatsübersicht Github Repositories Gitlab Repositories Mastodon Über mich home xmpp


Vor 5 Jahren hier im Blog

  • Fährnisse des Buildprozesses unter Windows

    17.07.2019

    Nachdem ich begonnen hatte, mich mit der Beschleunigung der Berechnung des Mandelbrot-Fraktals unter Zuhilfenahme der Shadereinheiten in Graphikkarten zu beschäftigen und erste Erfolge feiern konnte, wollte ich das mal auf einer richtigen Graphikkarte ausprobieren...

    Weiterlesen...

Neueste Artikel

  • Datenvalidierung UTF8 mit BiDi-Steuerzeichen (TrojanSource 2.0)

    Ich bin heute nochmal inspiriert worden, weiter über die Trojan Source Vulnerability nachzudenken. Meiner Meinung nach bestehen hier noch Probleme - speziell bei Nutzereingaben oder Daten, die über externe Schnittstellen ampfangen werden.

    Weiterlesen...
  • OpenStreetMap Navi als Docker-Container

    Ich habe die auf OpenStreetMap basierende OpenSource Navigationslösung Graphhopper in einen Docker-Container gepackt und als neuestes Mitglied in meinem Docker-Zoo willkommen geheißen.

    Weiterlesen...
  • SQL-Aggregatfunktionen in SQLite als BeanShell-Scripts

    Ich habe neulich über eine Möglichkeit berichtet, SQLite mittels der sQLshell und Beanshell-Skripten um SQL-Funktionen zu erweitern. In diesem Artikel versprach ich auch, über eine solche Möglichkeit für Aggregatfunktionen zu berichten.

    Weiterlesen...

Manche nennen es Blog, manche Web-Seite - ich schreibe hier hin und wieder über meine Erlebnisse, Rückschläge und Erleuchtungen bei meinen Hobbies.

Wer daran teilhaben und eventuell sogar davon profitieren möchte, muß damit leben, daß ich hin und wieder kleine Ausflüge in Bereiche mache, die nichts mit IT, Administration oder Softwareentwicklung zu tun haben.

Ich wünsche allen Lesern viel Spaß und hin und wieder einen kleinen AHA!-Effekt...

PS: Meine öffentlichen GitHub-Repositories findet man hier - meine öffentlichen GitLab-Repositories finden sich dagegen hier.