Ich wollte der WebEngine aus JavaFX eine html gerechte Textressource als Seite übergeben. Leider fordert sie entweder eine Adressangabe dieser Seite in Form eines Strings, der URL gerecht ist, oder die vollständige html gerechte Seite als String. Der zweite Fall ist nur zu empfehlen, wenn der erste Fall garnicht funktionieren will, da damit keine Historie möglich ist. Damit wollte ich unbedingt die Adressangabe als Mittel nehmen.


Leider traten beim Versuch immer wieder unerwartete Unverträglichkeiten auf. Ich entschloss mich deshalb, das Laden von Resourcen im modularen Java ab Version 9 genauer zu untersuchen. Dabei beschränke ich mich aber auf Text-, Bild- und Tonresourcen. Das Laden von *.class bleibt unberücksichtigt.

Welche Methoden aus welchen Klassen gibt es um solche Resourcen zu laden?

Als Methoden gibt es mehrere:
        InputStream        getResourceAsStream( String name );
                 - in den Klassen Module, Class und ClassLoader
        URL                getResource( String name );
                 - in den Klassen Class und ClassLoader
        static InputStream ClassLoader.getSystemResourceAsStream( String name );
                 - statische Methode im ClassLoader, dient eigentlich nur zum Laden von Klassen
                   und benutzt den SystemClassLoader.
                   Eigentlich sollen damit Dateien vom Typ *.class geladen werde.
        static URL         ClassLoader.getSystemResource( String name );        
                 - statische Methode im ClassLoader, der dann den SystemClassLoader benutzt.
                   Eigentlich sollen damit Dateien vom Typ *.class geladen werden.

außerdem gibt es noch die Methode, die ich aber nicht weiter untersuche
        static Enumeration<URL> getSystemResources​( String name )
                 - Sie sucht alle Ressourcen ( eigentlich vom Typ *.class ) im angegebenen Suchpfad.


Die Klasse Module ist erst mit der Modularisierung von Java eingeführt worden, die anderen beiden gab es schon immer, scheinen aber erweitert bzw geändert worden zu sein.

Aufpassen muss man beim Parameter. Hier ist (meistens) die Schreibweise wichtig. Grundsätzlich muss natürlich der Name mit Endung der Ressource übergeben werden, aber muss mit oder ohne Paketpfad, mit oder ohne einem führenden Schrägstrich angegeben werden.

Mit Paketpfad ist die Paketangabe mit Schrägstrichen statt der Punkte gemeint. Ohne führenden Schrägstrich nenne ich ihn relativ und mit führendem Schrägstrich absolut.


Die Ressource befindet sich bei den folgende Dingen immer im Modul 'm', dort im Paket 'de.firma.res' und hat den Namen 'datei.res'.

!1. Aufrufende Instanz und Resource befinden sich im gleichen Paket und damit auch im gleichen Modul.
(Es gibt unbenannte Module. Dürfen sich da gleichnamige Pakete befinden?) 
*Methode  'InputStream strom = getResourceAsStream( String name )'

**Module
    strom = getClass().getModule().getResourceAsStream( "de/firma/res/datei.res" )            ist ok
    strom = getClass().getModule().getResourceAsStream( "/de/firma/res/datei.res" )           ist ok
    strom = getClass().getModule().getResourceAsStream( "datei.res" )                         ist nicht ok
    
    Der Paketpfad muss mit angegeben werden. Dabei kann er absolut wie relativ angegeben sein.

**Class
     strom = getClass().getResourceAsStream( "de/firma/res/datei.res" )                       ist nicht ok
     strom = getClass().getResourceAsStream( "/de/firma/res/datei.res" )                      ist ok
     strom = getClass().getResourceAsStream( "datei.res" )                                    ist ok
    
     Findet die Datei ohne Paketpfad und mit absolutem Paketpfad.

**ClassLoader
     strom = getClass().getClassLoader().getResourceAsStream( "de/firma/res/datei.res" )      ist nicht ok
     strom = getClass().getClassLoader().getResourceAsStream( "/de/firma/res/datei.res" )     ist nicht ok
     strom = getClass().getClassLoader.getResourceAsStream( "datei.res" )                     ist nicht ok
  - 
     Es klappt mit dem ClassLoder nur, wenn die Datei ins oberste Verzeichnis des Moduls verlegt wird
     und dann nur der Dateiname übergeben wird.

**SystemClassLoader
     strom = ClassLoader().getResourceAsStream( "de/firma/res/datei.res" );                   ist nicht ok
     strom = ClassLoader().getResourceAsStream( "/de/firma/res/datei.res" );                  ist nicht ok
     strom = ClassLoader().getResourceAsStream( "datei.res" );                                ist nicht ok

     Befinden sich beide im gleichen Paket, wird nichts gefunden.

*Methode  'URL url = getResource( String name )'
 
**Module
      Ein Modul hat keine Methode getResource( String name );
 
**Class
      url = getClass().getResource( "de/firma/res/datei.res" );                               ist nicht ok
      url = getClass().getResource( "/de/firma/res/datei.res" );                              ist ok
      url = getClass().getResource( "datei.res" );                                            ist ok

      Findet die Datei ohne Paketpfad und mit absolutem Paketpfad.

**ClassLoader der Klasse
      url = getClass().getClassLoader().getResource( "de/firma/res/datei.res" );              ist nicht ok
      url = getClass().getClassLoader().getResource( "/de/firma/res/datei.res" );             ist ok
      url = getClass().getClassLoader().getResource( "datei.res" );                           ist ok

      Findet die Datei ohne Paketpfad und mit absolutem Paketpfad.

**SystemClassLoader
      url = ClassLoader.getSystemResource( "de/firma/res/datei.res" );                        ist nicht ok
      url = ClassLoader.getSystemResource( "/de/firma/res/datei.res" );                       ist nicht ok
      url = ClassLoader.getSystemResource( "datei.res" );                                     ist nicht ok

      Befinden sich beide im gleichen Paket, wird nichts gefunden.
                 
!2. Aufrufende Instanz und Ressource befinden sich in verschiedenen Paketen, aber gleichem Modul bzw jar-Datei.

*Methode  'strom = getResourceAsStream(...)'.

**Modul
      strom = getClass().getModul().getResourceAsStream( "de/firma/res/datei.res" );          ist ok
      strom = getClass().getModul().getResourceAsStream( "/de/firma/res/datei.res" );         ist ok
      strom = getClass().getModul().getResourceAsStream( "datei.res" );                       ist nicht ok
      strom = getClass().getModul().getResourceAsStream( "../../../de/firma/res/datei.res" ); ist nicht ok
         Die Anzahl der ../ hängt vom Paketpfad des aufrufenden Objektes ab

      Der Paketpfad muss mit angegeben werden. Dabei kann er absolut wie relativ angegeben sein.

**Class
      strom = getClass().getResourceAsStream( "de/firma/res/datei.res" );                     ist nicht ok
      strom = getClass().getResourceAsStream( "/de/firma/res/datei.res" );                    ist ok
      strom = getClass().getResourceAsStream( "datei.res" );                                  ist nicht ok
      strom = getClass().getResourceAsStream( "../../../de/firma/res/datei.res" );            ist nicht ok
         Die Anzahl der ../ hängt vom Paketpfad des aufrufenden Objektes ab

      Findet die Datei nur mit Angabe des absoluten Paketpfades.
      
**ClassLoder
      strom = getClass().getClassLoader().getResourceAsStream( "de/firma/res/datei.res" );          ist nicht ok
      strom = getClass().getClassLoader().getResourceAsStream( "/de/firma/res/datei.res" );         ist nicht ok
      strom = getClass().getClassLoader().getResourceAsStream( "datei.res" );                       ist nicht ok
      strom = getClass().getClassLoader().getResourceAsStream( "../../../de/firma/res/datei.res" ); ist nicht ok
         Die Anzahl der ../ hängt vom Paketpfad des aufrufenden Objektes ab


      Den ClassLoader sollte man in Java mit Javamodulen lieber vergessen.
      Ich habe nicht geprüft, ob die Datei gefunden wird, wenn sie im obersten Paket des Modules ist.

 **SystemClassLoader
      strom = ClassLoader().getResourceAsStream( "de/firma/res/datei.res" );                  ist nicht ok
      strom = ClassLoader().getResourceAsStream( "/de/firma/res/datei.res" );                 ist nicht ok
      strom = ClassLoader().getResourceAsStream( "datei.res" );                               ist nicht ok

      Auch in verschiedenen Paketen wird nichts gefunden( .res ist ja auch keine Klasse )

*Methode  'url = getResource( String name )'

**Modul
      Ein Modul hat keine Methode getResource(...);

**Class
      url = getClass().getResource( "de/firma/res/datei.res" );                               ist nicht ok
      url = getClass().getResource( "/de/firma/res/datei.res" );                              ist ok
      url = getClass().getResource( "datei.res" );                                            ist nicht ok
      url = getClass().getResource( "../../../de/firma/res/datei.res" );                      ist nicht ok
         Die Anzahl der ../ hängt vom Paketpfad des aufrufenden Objektes ab

      Findet die Datei nur mit Angabe des absoluten Paketpfades.

**ClassLoader
      url = getClass().getClassLoader().getResource( "de/firma/res/datei.res" );              ist nicht ok
      url = getClass().getClassLoader().getResource( "/de/firma/res/datei.res" );             ist nicht ok
      url = getClass().getClassLoader().getResource( "datei.res" );                           ist nicht ok
      url = getClass().getClassLoader().getResource( "../../../de/firma/res/datei.res" );     ist nicht ok
         Die Anzahl der ../ hängt vom Paketpfad des aufrufenden Objektes ab

      Siehe Bemerkung oben bei getResourceAsStream(...).


**SystemClassLoader
      url = ClassLoader.getSystemResource( "de/firma/res/datei.res" );   // ist ok
      url = ClassLoader.getSystemResource( "/de/firma/res/datei.res" );  // ist nicht ok
      url = ClassLoader.getSystemResource( "datei.res" );                // ist nicht ok

      Ein erstaunliches Ergebnis!
      Befinden sich aufrufende Klasse und Ressource im gleichen Paket, wird nichts gefunden.
      Sind sie aber in verschiedenen Paketen, wird sie mit relativem Paketpfad gefunden.

!3 Aufrufende Instanz und Ressource befinden sich in verschiedenen Modulen.

Hat man das zugehörige Objekt der Klasse Module vom Modul 'm', ich nenne es 'modul' oder ein Klassenobjekt einer im Modul 'm' befindlichen Klassen, ich nenne sie 'klasse', und kennt man das Paket, indem sich die Resource befindet, führen folgende Aufrufe zum Erfolg. Das Modul 'm' muss allerdings auch im Modulpfad der Anwendung existieren.

Dann geht es genau wie oben mit der Methode getResourceAsStream(...) aus dem Module;

      InputStream strom = modul.getResourceAsStream( Paketpfad + name der Datei im Modul modul );
      
oder mit Hilfe des Klassenobjektes aus dem Modul:
   
      InputStream strom = klasse.getModule().getResourceAsStream( Paketpfad + name der Datei im Modul modul );

      InputStream strom = klasse.getResourceAsStream( Paketpfad + name der Datei im Modul modul );

      URL url = klasse.getResource( absoluter Paketpfad + name der Datei im Modul von 'klasse' );
      
Ebenfalls zum Erfolg führt folgendes sogar bei unbenannten Modulen. Eine jar-Datei, die nicht modularisiert ist, muss per Classpath eingebunden sein.
      URL url = ClassLoder.getSystemResource( relativer Paketpfad + name der Datei im Modul der Resource );
      URL url = ClassLoder.getSystemResourceAsStream( relativer Paketpfad + name der Datei im Modul der Resource );
      
Da wo der Paketpfad kein Attribut hat, darf er relativ wie auch absolut sein.
 
!Ergebnis:

Man ist auf der sichersten Seite, wenn man die Methoden aus dem Class-Objekt benutzt und im Parameter den absoluten Paketpfad mit angibt. Hat man allerdings weder ein Modul- noch eine Class-Objekt, da das Modul nur Resourcen enthält und damit auch keine Klasse, dann muss man ClassLoader.getSystemResource(...) oder ClassLoader.getSystemResourceAsStream(...) benutzen. Dabei ist es egal, ob es ein benanntes oder unbenanntes Modul ist. Es wird sich nach der jar-Datei gerichtet und intern Klasse JarUrlConnection benutzt.
Was nun allerdings passiert, wenn ich all diese einzelnen jar-Dateien in einer einzigen jar-Datei zusammenführe, habe ich noch nicht ausprobiert.


--------------------------------------------------------------------------------------------------------------------



Mit dem Eingangsstrom kann man nur die Resource einlesen und komplett übergeben oder dem Objekt, das die Resource zur gedachten Darstellung bringen soll, kann man den Eingangsstrom als Parameter übergeben.
   
In Swing nehmen viele solcher Objekte ein URL-Objekt oder File-Objekt, das man auch aus dem URL-Objekt bilden kann, an.
   
Mit url.toExternalForm() erhält man die URL-Adresse als String, wie es bei vielen Klassen in JavaFX gefordert wird.
   
Wer die Mühe nicht scheut, kann die URL-Adresse auch von Hand als String angeben:
Sie beginnt mit "jar:file://", dann folgt das absolute Verzeichnis aus dem Dateisystem bis zum Verzeichnis der jar-Datei, anschließend kommt der Name der jar-Datei mit abschließendem Ausrufungszeichen, dann der absokute Paketpfad und zum Schluss der Name der Resource. Es gibt im Internet auch viele Beschreibungen, wo der doppelte Schrägstrich hinter "jar:file:" fehlt. Ob das auch in Ordnung ist, habe ich nicht ausprobiert. Im einfachen Fall reicht ein einfaches "file:" vor der Pfadangabe und dem Namen. 'uri.toExternalForm()' jedenfalls gibt immer das doppelte Schrägzeichen aus. Es fügt auch immer ein "./" zwischen dem Verzeichnis, aus dem java die Anwendung gestartet hat, und dem Rest des Verzeichnisses bis zur jar-Datei ein, was eindeutig weggelassen werden darf.
      
Es gibt noch das Problem, dass man jar-Dateien einbinden kann, die nicht nach Art von Java modularisiert sind. Hier muss man wohl ein Objekt der Klasse 'JarUrlConnection' oder die statischen Methoden des ClassLoaders bzw den SystemClassLoader benühen.

Kai Ehlers