Javas Modulsystem: Hilfe, meine Abhängigkeiten sind keine Java-Module!

Mit Java kann man Anwendung mittlerweile gut modularisieren, muss aber auch Abhängigkeiten beachten. Wenn diese keine Java-Module sind, wird es spannend.

In Pocket speichern vorlesen Druckansicht 17 Kommentare lesen

(Bild: Shutterstock)

Lesezeit: 4 Min.
Von
  • Hendrik Ebbers

In einem vorherigen Post habe ich über eine minimale Unterstützung für das Java Modulsystem (Java Platform Module System, JPMS) geschrieben und wie man helfen kann, sie zu erreichen. Nun kann es aber immer wieder passieren, dass Bibliotheken das Java-Modulsystem nicht unterstützen und es auch nicht absehbar ist, dass diese in Zukunft wenigstens als „Automatische Module“ verfügbar sein werden.

Neuigkeiten von der Insel - Hendrik Ebbers

Hendrik Ebbers (@hendrikEbbers) ist Java Champion, JCP Expert Group Member und wurde mehrfach als Rockstar-Speaker der JavaOne ausgezeichnet. Mit seinem eigenen Unternehmen Open Elements hilft Hendrik aktuell den Hedera Hashgraph zu gestalten und dessen Services der Öffentlichkeit zugänglich zu machen. Hendrik ist Mitgründer der JUG Dortmund sowie der Cyberland und gibt auf der ganzen Welt Vorträge und Workshop zum Thema Java. Sein Buch "Mastering JavaFX 8 Controls" ist 2014 bei Oracle Press erschienen. Hendrik arbeitet aktiv an Open Source Projekten wie beispielsweise JakartaEE oder Eclipse Adoptium mit. Hendrik ist Mitglied des AdoptOpenJDK TSC und der Eclipse Adoptium WG.

Wenn man seinen eigenen Code auf das JPMS umstellen will und von solchen Bibliotheken abhängig ist, muss man teilweise in die Trickkiste greifen. In diesem Post möchte ich einmal auf genau solche Abhängigkeiten eingehen und schauen, wie man damit umgehen kann.

Auch wenn ich mich eher bei Maven zu Hause fühle, habe ich erst neulich an der Umstellung eines großen Gradle-Projekts auf Java-Module gearbeitet. Da das Projekt Open Source ist, kann es einfach bei GitHub eingesehen werden. In diesem Projekt gab es zu Beginn des Umbaus eine Vielzahl von Abhängigkeiten, die nicht das Java-Modulsystem unterstützt haben. Bei einigen konnten wir eine nachhaltige Lösung erzielen, indem wir direkt Pull Requests (PR) bei den jeweiligen Projekten erstellt haben, um diese um einen Automatic-Module-Name zu ergänzen. Wie man das einfach über ein Maven- oder Gradle-Plug-in erreichen kann, habe ich bereits im vorherigen Post zu dem Thema beschrieben. Ein Beispiel eines solchen PR kann hier gefunden werden.

Nun gibt es aber auch Abhängigkeiten, bei denen man einen solchen PR nicht einfach stellen kann oder bei denen der PR nicht angenommen wird. Vielleicht hat man auch eine Abhängigkeit, deren Weiterentwicklung eingestellt wurde. In allen diesen Fällen wird eine andere Umsetzung benötigt. Im Grunde muss man sich selbst darum kümmern, aus den Abhängigkeiten Java-Module zu erstellen. Hierfür gibt es unterschiedliche Möglichkeiten. Man kann beispielsweise händisch einen Automatic-Module-Name-Eintrag zum Manifest des Jar hinzufügen und die abgeänderte Version dann in einem internen Maven Repository hosten. Für das erwähnte Gradle-Projekt haben wir auf das „extra-java-module-info“-Plug-in von Jendrik Johannes zurückgegriffen. Dieses als Open Source verfügbare Plug-in erlaubt es, zur Build-Zeit einen Automatic-Module-Name Eintrag zu Abhängigkeiten hinzuzufügen. Konkret wird das Plug-in wie in folgendem Beispiel genutzt, wobei bei jedem automaticModule(…) Aufruf der Gradle Identifier der Abhängigkeit als erster Parameter und der zu nutzende Modulname als zweiter Parameter übergeben werden:

plugins {
    id("org.gradlex.extra-java-module-info")
}

extraJavaModuleInfo {
    failOnMissingModuleInfo.set(true)

	automaticModule("io.prometheus:simpleclient", 
	                "io.prometheus.simpleclient")
	automaticModule("io.prometheus:simpleclient_common",
	                "io.prometheus.simpleclient_common")
	automaticModule("io.prometheus:simpleclient_httpserver", 
	                "io.prometheus.simpleclient.httpserver")
}

Zusammen mit dem Autor des Plug-ins konnten wir in einem sehr produktiven Austausch das Ganze sogar noch deutlich erweitern. Das Plug-in konnte neben automatischen Modulen schon immer Module mit einer module-info.java erstellen. Hierbei musste man allerdings händisch Angaben wie exports definieren. Durch neue Funktionalitäten kann man nun ein Modul so definieren, dass dessen vollständigen Packages exportiert werden (siehe weitere Infos). Das hat den großen Vorteil, dass man nicht mit automatischen Modulen arbeiten muss, die einige Besonderheiten mich sich bringen, da unter anderem alle automatischen Module zu den „required“ Abhängigkeiten eines Moduls hinzugefügt werden, sobald ein automatischen Modul als „required“ in der module-info.java angegeben ist (siehe Java Spec). Hier noch einmal ein großes Dankeschön an Jendrik Johannes als Maintainer der Bibliothek. Unsere Zusammenarbeit hat aus meiner Sicht extrem gut die Vorteile von Open Source aufgezeigt. Für alle, die hier noch tiefer einsteigen möchten, hat Jendrik mehrere Videos zu diesem Thema und anderen Themen rund um Gradle kostenlos auf YouTube gehostet.

Ein letztes großes Problem kann aber auch mit den hier vorgestellten Umsetzungen nicht behoben werden: Sobald ein JAR gegen die package split constraints des Java-Modulsystems verstößt, kann es nicht zum modulepath hinzugefügt werden. In diesem Fall müssen noch deutlich weitreichende Schritte vorgenommen werden. Diesen Punkt werde ich mich aber in einem zukünftigen Beitrag zuwenden.

(rme)