Java 9 bringt eine neue Exception Klasse mit. Ist das Grund zur Freude? Ich finde ja! Mit diesem Blogpost möchte ich euch erklären, wie wir mit der neu dazugekommenen Ausnahme InaccessibleObjectException umgehen.
Zu allererst ein wenig Theorie. Wie man der offiziellen API entnehmen kann, gibt es die InaccessibleObjectException Klasse erst seit Java 9. Grund der Einführung ist das neue Modulsystem "Jigsaw". Die spannende Frage ist nun: Warum gibt es diese Ausnahme und wann kann diese auftreten? Das erkläre ich am besten mit einem kurzen Beispiel.
Folgende Klasse befindet sich im Projekt "ServiceProject". Diese besitzt zwei Methoden. Eine öffentlich erreichbare Methode und eine von außen nicht sichtbare private Methode.
Da es sich um ein auf Java 9 aufbauendes Projekt handelt, nutzen wir natürlich auch die Möglichkeit unsere Software mittels Modulen zu strukturieren. Die Modul-Definition sieht folgendermaßen aus:
Kein Service ohne Konsument. Das zweite Java Projekt trägt den Namen ConsumerProject. Dieses Projekt besitzt ebenfalls eine Modul-Definition mittels einer modul-info.java. Hier ist angegeben, dass das serviceModul benötigt wird. Wir möchten die Funktionalität des bereitgestellten Services im Konsumenten aufrufen.
In der main Methode wird ein Service Objekt erzeugt und die doSomethingPublic() Methode aufgerufen. Das ist natürlich kein Problem, da es sich hier um eine public Methode handelt. Weiter soll auch die doSomethingPrivate() Methode ausgeführt werden. Da diese von außen nicht sichtbar ist, müssen wir Reflection einsetzen, um diese Methode aufrufbar zu machen und sie dann ausführen zu können. Das Exception-Handling, welches aufgrund des Einsatzes von Reflection-Funktionalität notwendig ist, habe ich aus Gründen der Übersichtlichkeit weggelassen.
Ab jetzt wird es spannend, wenn wir versuchen die main Methode auszuführen. Ein Problem mit der doSomethingPublic() gibt es erwartungsgemäß natürlich nicht. Aber die doSomethingPrivate() Methode per Reflection aufzurufen, führt zu einem Fehler. Und diese Ausnahme wird durch die InaccessibleObjectException signalisiert. Hier der Inhalt der Konsole inklusive der Exception:
Nun stellt sich allerdings die Frage, warum es zu dieser Exception kommt. Grund dafür ist das Standard-Verhalten des Modul-Systems in Java 9. Wird ein Modul per requires eingebunden, bedeutet es nicht automatisch, dass per Reflection nicht zugreifbare Typen erreichbar gemacht werden können. Um das zu können, braucht es das neue Schlüsselwort opens. Dieses muss von dem Service Entwickler in der Modul-Definition gesetzt werden.Vor Java 9 konnte man als Entwickler eines Projektes nicht verhindern, dass per Reflection nicht zugreifbare Typen und Methoden sichtbar und somit ausführbar gemacht wurden.
Hier die angepasste module-info.java des Service Projektes inklusive der opens Angabe.
Nachdem das erledigt ist, kann die main Methode erneut ausgeführt werden. Und siehe da! Die private doSomethingPrivate() Methode kann nun wieder per Reflection ausgeführt werden. Hier die Konsolenausgabe:
Und nun könnt ihr meine eingangs erwähnte Euphorie bezüglich der Exception besser nachvollziehen. Denn das Modul-System alleine ist bereits ein Feature, welches ich nicht mehr missen möchte. Dass es aber gleichzeitig noch möglich ist, Reflection aktiv zu verhindern, ist eine sinnvolle und dringend notwendige Neuerung. Zugriffe auf z. B. interne Java-APIs können somit unterbunden werden, was einen erheblichen Sicherheitsgewinn darstellt.
Einziger Wermutstropfen ist aktuell noch, dass man die Verhinderung des Einsatzes von Reflection an internen APIs umgehen kann. Oracle erreicht dadurch, dass viele Projekte noch weiterhin funktionieren. Dadurch wird eine gewisse Abwärtskompatibilität gewährleistet. Allerdings wird es diesen Kill Switch nicht für immer geben.