BLEIBEN SIE INFORMIERT
mit den ORDIX Newslettern!

Melden Sie sich für unsere Newsletter an und erhalten Sie exklusive Updates zu IT-Trends, spannenden Seminaren und Neuigkeiten der ORIDX AG.

BLEIBEN SIE INFORMIERT
mit den ORDIX Newslettern!

Melden Sie sich für unsere Newsletter an und erhalten Sie exklusive Updates zu IT-Trends, spannenden Seminaren und Neuigkeiten der ORDIX AG.

7 Minuten Lesezeit (1333 Worte)

Neuheiten Java 9 (Teil I) Java 9 - Was lange währt

Wer kann sich noch erinnern? Im März 2014 kam das letzte Java Major Release Java 8 und es gab sehr viel Neues, wobei sich vor allem Lambda-Ausdrücke und die mächtige Stream- API hervortaten. Am 21. September 2017 kam nun endlich das mit Spannung erwartete Java 9 heraus, um mit der Hauptneuerung „Modularisierung" aufzuwarten. 

Was bringt Java 9, warum hat es so lange gedauert und wie können wir am besten vom neuen Release profitieren? Das wollen wir hier und in weiteren Artikeln zum Thema machen. Zunächst fokussieren wir uns auf das folgenreichste neue Feature: Die Modularisierung (läuft bei Oracle unter dem Codenamen „Jigsaw"). Sie geht verschiedene Problematiken im Java-Umfeld an, die sich grob in die Kategorien Sicherheitslücken, JAR-Hell und Spaghetti-Projekte einsortieren lassen.

Modularisierung –
Gab es das nicht schon vorher?

Modularisierung in Java gab es in gewisser Weise schon immer, aber diese beschränkt sich vorwiegend auf Quellcode- Ebene. Jeder kennt die Einteilung in Klassen, die – grob gesprochen – aus einem öffentlich zugänglichen Teilbereich (public) und einen verborgenen, privaten (private) bestehen. Klassen und Interfaces sind ihrerseits wieder in Packages angeordnet, die ebenfalls bezogen auf Sichtbarkeit steuerbar sind (package-visibility). Das ist soweit schön und gut, in der Praxis fehlen aber noch ganz wichtige Zutaten. Wir kommen später noch genauer auf public und die neuen, damit einhergehenden Einschränkungen zu sprechen, die durch Jigsaw Einzug in das JavaÖkosystem halten.

Leider müssen wir nämlich schmerzlich akzeptieren, dass diese Aufteilungs- und Strukturierungsmöglichkeiten mit dem build-Vorgang schlagartig verloren gehen, denn typischerweise gelangt compilierter Bytecode in Archivdateien (*.jar, *.war). Deren Namensgebung ist weitgehend beliebig. Diese Dateien werden deployed oder allgemeiner gesprochen ausgeliefert und sie repräsentieren häufig die eigentliche Applikation. Zur Laufzeit werden nun die Klassen und Interfaces aus den Archivdateien vom Classloader geladen und die Gesamtheit aller geladenen Java Konstrukte machen die lauffähige Anwendung aus.

Die Problematik resultiert aus mangelnden Vorgaben und Regelungsmöglichkeiten zum Aufbau und zur Struktur von solchen Archivdateien. In einer JAR-Datei können prinzipiell beliebig viele und beliebig abhängige Java-Klassen untergebracht sein. Sobald sie geladen sind, geht die Information, aus welcher JAR-Datei sie stammen, verloren.

Die JAR-Hell- und Security-Einfallstore

Worin besteht denn nun genau das Problem und was haben JAR-Dateien damit zu tun? Es kommt zu einem großen Bruch beim Übergang von Quellcode zum Binärformat, weil es die Eigenschaft der Kapselung so nicht bei Java-Artefakten gibt (wie man JAR-Dateien auch nennt). Daraus folgt zum Beispiel, dass sich sensibler Code nur schlecht schützen lässt sobald er im Binärformat vorliegt. Und das Binärformat kommt praktisch überall vor, selbst das JDK (Java Devolpment Kit) ist im Wesentlichen nichts anderes als eine Ansammlung von Archivdateien (z.B. $JRE_HOME/lib/rt.jar, welches die Java-Kernklassen beinhaltet).

Diese mangelnde Schutzmöglichkeit musste selbst Oracle schmerzlich erfahren, als mit dem Erwerb von Java und der Auslieferung von Java 7 eine Vielzahl von Sicherheitslücken offenkundig wurden und es zahlreicher Patches bedarf, diese zu schließen. Denn sicherheits kritische Codestellen waren in den JDK-Archiven mit relativ einfachen Mitteln (Stichwort Reflection) lesbar, untersuchbar und schlimmstenfalls manipulierbar. Schadsoftware hatte dadurch relativ leichtes Spiel und leichten Zugang zu Java-Kernklassen. Das lag u.a. an der einfachen Möglichkeit, die $JRE_HOME/lib/rt.jar und andere JAR-Dateien mit einem prinzipiellen Zugriffsschutz auszustatten, was dann auch von der kriminellen Hackerszene weidlich ausgenutzt wurde.

Module weisen den Weg ins Glück

Das konnte so nicht weitergehen, und Oracle hat einen sehr langen Weg hin zu einer Modularisierung des gesamten JDK beschritten. Dieser dauerte 5 Jahre bis zur Version Java 9. Das Ergebnis ist eine komplett geänderte Dateistruktur des JRE (Java Runtime Environment), was z.B. an dem Wegfall der Datei $JRE_HOME/lib/rt.jar erkennbar wird. Statt einer Handvoll übervoller JARDateien, haben wir es ab Java 9 mit einem Modulsystem von über 100 Modulen zu tun.
Zur erheblichen Verzögerung von Java 9 hat auch die durchaus kontrovers geführte Diskussion um das richtige Konzept zur Modularisierung beigetragen. Kurz vor dem eigentlich geplanten Releasetermin Juli 2017 verweigerten Red Hat und IBM ihre Zustimmung zum Jigsaw-Vorschlag von Mark Reinhold, dem Java-Chefarchitekten von Oracle. Das Konzept wurde nochmals dahingehend überarbeitet, um einen „weicheren" Migrationspfad von alten Java-Anwendungen zu Java 9 zu ermöglichen, wodurch dem Erscheinungstermin am 21. September 2017 nichts mehr im Wege stand.
Was hat es denn nun mit Jigsaw auf sich? Der Vorgang der Modularisierung erzeugt Module, und diese zeichnen sich durch drei Eigenschaften aus, welche die Praxistauglichkeit ausmachen. Ein Modul hat:

  • Einen Namen oder eine eindeutige ID 
  • Abhängigkeiten: Was braucht mein Modul, wovon bin ich abhängig? 
  • Nutzbare API: Was kann ich anbieten, wie sieht die öffentliche Schnittstelle aus?

Wichtige Konzepte des JPMS

Das Modulsystem von Java 9 (JPMS – Java Platform Module System) eröffnet den Entwicklern die Möglichkeit, Artefakte (JAR-Dateien) als Module und die oben erwähnten drei Eigenschaften zu definieren. Module stellen damit eine vollkommen neue, zusätzliche Strukturierungsmöglichkeit in Java 9 zur Verfügung. Bislang waren es Packages, die die größtmögliche Klammer bildeten, unter der sich zusammengehörige Klassen, Interfaces, Enums, etc. gruppierten.

Im neuen Java 9 kommen noch Module dazu, die viele alte Ordnungsprinzipien und Regeln neu ausrichten. Ein Modul ist ein abgeschlossener „Behälter", in dem sich zusammenhängende Klassen, Properties und nativer Code versammeln und Abhängigkeiten von Codeteilen drücken sich auf Modulebene aus (siehe Abbildung 1). Ein Modul-Deskriptor beschreibt diese Abhängigkeiten, gehört damit ebenfalls zum Modulinhalt und befindet sich im root-Verzeichnis des Modul-Directory.

Beispielhaft zu sehen ist das im kurzen Codeblock in der Abbildung 2. Dort ist das Modul mit dem Namen myapp.mod1 beschrieben (identity, Eigenschaft 1), zusammen mit der Abhängigkeit zu einem anderen Modul myapp.mod2 (requires, Eigenschaft 2) und mit einem eigenen zur Verfügung gestellten Paket de.ordix.pkg1 (exports, Eigenschaft 3). Das ist im Übrigen kein Rechtschreibfehler, requires erfordert Module, exports stellt Packages zur Benutzung bereit.

Wo aber hat denn so ein Codeblock zu stehen? Es handelt sich schließlich um nichts Geringeres als eine Moduldefinition. Genau das ist es auch und dafür wird eine spezifische Source-Datei mit dem Namen module-info.java vorgeschrieben (siehe Abbildung 3). Es handelt sich um den zuvor erwähnten Modul-Deskriptor, den der Compiler wie jede andere Java-Source transformiert und zwar zu module-info.class.
Abb. 1: Grundzüge der Modulabhängigkeiten. Packages stellt man per exports zur Verfügung. Mittels requires drückt man den Bedarf an anderen Modulen aus
module myapp.mod1 {
   requires mod2;
   exports de.ordix.pkg1;
   exports de.ordix.pkg2;
}
module myapp.mod1 {
   exports de.ordix.pkgA;
} 

Abb. 2: Die Inhalte von 2 Moduldeskriptoren, abgelegt in den jeweiligen Verzeichnissen in den Deskriptor Dateien modul-info.java

src/myapp.mod1/
    module-info.java
    de.ordix.pkg1/
      ClassA.java
      ClassB.java
...
    de.ordix.pkg2/
      ClassC.java 
Abb. 3: Verzeichnis eines Moduls mit der Deskriptor Datei auf oberster Ebene und den Packages, die sich im Modul befinden

Die Wirkungsweise von JPMS

Um die Vorteile von JPMS erkennen zu können, müssen wir einen genauen Blick auf dessen Bedeutung und Semantik werfen. Das Schlüsselwort public hat früher signalisiert: Die Klasse oder das Interface ist global im Anwendungskontext nutzbar, sofern das zugehörige Package per import herangezogen wurde. Im neuen Java 9 heißt public: Eine so spezifizierte Klasse ist nur innerhalb des Moduls von überall ansprechbar. Von einem anderen Modul nur genau dann, wenn das zugehörige Package mit der Klasse per exports freigegeben wird und das andere Modul es per requires anfordert. Das ist revolutionär, denn es bringt – endlich muss man sagen – echten Zugriffsschutz. Eine public-Klasse, die nicht exportiert wird, kann von außerhalb des Moduls nicht angesprochen werden, auch nicht über Reflection-Mechanismen!

Fazit

Java 9 ist nach langer Zeit nun endlich da und wir haben die wichtigste Neuerung unter die Lupe genommen: Modularisierung auf Artefaktebene. Diese Möglichkeit erlaubt dem Entwickler, auf JAR-Dateien bzw. auf dem Auslieferungspaket einer Java-Anwendung die wichtigen Prinzipien der „Strong Encapsulation" und „Reliabale Configuration" anzuwenden. Dadurch gelingt es zum einen aus der JAR-Hell auszubrechen und zum anderen, potenzielle Sicherheitslücken zu vermeiden. Wie schon angesprochen umfasst Java 9 eine Vielzahl von Neuerungen, auf die wir in zukünftigen Artikeln eingehen wollen. Selbstverständlich wird es in Kürze eine Seminarveranstaltung zu „Java 9 Neuheiten" bei der ORDIX AG geben. Dort vermitteln wir Ihnen als Seminarteilnehmer sämtliche Neuerungen zu Java 9.

Glossar

JPMS
Das Java Platform Modular System ist die neue Strukturierungsmöglichkeit auf Ebene von JAR-Dateien.

Links/Quellen

Senior Chief Consultant bei ORDIX

Ähnliche Beiträge

 

Kommentare

Derzeit gibt es keine Kommentare. Schreibe den ersten Kommentar!
Freitag, 27. Juni 2025

Sicherheitscode (Captcha)