AuM
I'm new here

Classloading Probleme in der Modulentwicklung

Hallo Zusammen,

vorweg der Hinweis: dieser Post richtet sich an diejenigen, die FirstSpirit Modulentwicklung betreiben und dabei externe Bibliotheken (Hibernate, RESTeasy, etc.) verwenden. Alle anderen können  an dieser Stelle beruhigt aufhören zu lesen ;-).

Wir hatte im Zuge der Modul-Entwicklung  diverse Konflikte mit dem FirstSpirit Classloader. In dieser Community finden sich dazu auch diverse Posts (https://community.e-spirit.com/message/18406 https://community.e-spirit.com/message/14123#14123 https://community.e-spirit.com/message/11055 etc.).

Im Folgenden würde ich euch gerne zwei Probleme kurz vorstellen und dann zeigen wie ein Maven-Plugin diese Probleme lösen kann - vielleicht hilft es euch ja.

Problem 1: Benötigte Bibliotheken sind bereits im FirstSpirit Server (in einer anderen Version) enthalten

Der FirstSpirit Server enthält intern einige Fremdbibliotheken (z.B. Apache HTTP Client, Apache POI, etc.), wenn ihr nun in eurem Modul zwingend eine andere Version einer dieser Bibliotheken braucht habt ihr ein Problem!

Der FirstSpirit Classloader ist hierarchisch aufgebaut (siehe ModuleDeveloper PDF, Kapitel 2.10), sucht standardmäßig zuerst im Server Classloader und findet daher nur die Version, die mit FirstSpirit ausgerollt ist. Die eigene Version innerhalb eures Moduls wird nicht gefunden!

Problem 2: Beim Zugriff auf Ressourcen (z.B. Properties Dateien) werden diese nicht gefunden

Falls ihr innerhalb von FirstSpirit Modulen nicht auf Ressourcen zugreifen könnt, die innerhalb von JAR Dateien liegen (z.B. Konfigurationsdateien), dann kann es an einem Problem mit dem FirstSpirit Classloader liegen (siehe z.B. https://community.e-spirit.com/message/20380). Bei Modulen die mehrere JARs enthalten kommt es je nachdem wie auf die Ressourcen zugegriffen wird (Thread Classloader vs. Klassen Classloader) zu Problemen. Wir vermuten, dass der Classloader in der falschen JAR sucht - zumindest hat eine Debugging Session dies nahegelegt. Dies betrifft z.B. die bekannten Bibliotheken für Datenbankzugriff (Hibernate, EclipseLink, etc.) oder Webservice Clients (RESTeasy, etc.).

Das kritische an dem Fehler ist, dass er oftmals nicht reproduzierbar auftritt oder sich nach einem Server / Client Neustart anders verhält. Ein Kollege und ich hatten sogar die Situation, dass bei der Nutzung vom RESTeasy Client der Fehler bei einem von uns auftritt und bei dem anderen nicht. Nach einem Neustart war es genau andersherum.

Glücklicherweise haben wir für die beiden Probleme mit dem Maven Shade Plugin eine Lösung gefunden, die komplett ohne Quellcode-Anpassung sowie Nutzung anderer Bibliotheken auskommt. Im Grund macht es zwei Dinge:

  1. Zusammenfassen aller Abhängigkeiten und der eigenen Java-Klassen zu einer einzigen JAR Datei
  2. Umziehen von Java-Packages zwischen Kompilierung und Paketierung (z.B. org.apache.http -> meine.abhaengigkeit.org.apache.http)

Durch diese beiden Anpassungen verhindert es effektiv das Suchen des Classloaders in der falschen JAR Datei. Durch die Änderung der Packagenamen wird es sogar ermöglicht eine eigene Version einer Bibliothek zu nutzen, die bereits im FirstSpirit Server vorhanden ist (Durch die Änderung der Namen werden Konflikte aufgelöst).

Glücklicherweise benötigt das Plugin dabei keine Anpassung des Codes, sowohl die Neupaketierung als auch die Namensänderung erfolgen transparent im Buil-Prozess.

Nachfolgend findet ihr einen exemplarischen Konfigurations-Schnipsel zum Einbauen in eure Build-Routinen (falls benötigt), weitere Informationen findet ihr unter: http://maven.apache.org/plugins/maven-shade-plugin/

<!-- Relocate dependency classes to avoid library conflicts with FirstSpirit 5.1 and bundle everything in one JAR to avoid FirstSpirit class loading conflicts. -->
<plugin>
  
<groupId>org.apache.maven.plugins</groupId>
  
<artifactId>maven-shade-plugin</artifactId>
  
<version>2.3</version>
  
<executions>
     
<execution>
        
<phase>package</phase>
        
<goals>
           
<goal>shade</goal>
        
</goals>
        
<configuration>
           
<relocations>
              
<relocation>
                 
<pattern>org.apache</pattern>
                 
<shadedPattern>com.myorganisation.dependency.org.apache</shadedPattern>
              
</relocation>
           
</relocations>
           
<shadedArtifactAttached>true</shadedArtifactAttached>
           
<shadedClassifierName>jar-with-dependencies</shadedClassifierName>
        
</configuration>
     
</execution>
  
</executions>
</plugin>

Falls ihr eines der Probleme habt und noch Fragen offen sind, schießt los!

Viel Spaß damit und schönes Wochenende

Martin

P.S.

Falls ihr elegantere Versionen kennt diese Probleme zu umgehen oder mehr Details zu den Ursachen habt, dann immer raus damit!

2 Replies
jsp
I'm new here

Moin Moin,

für meinen Fall musste ich noch weitere Einstellungen am maven-shade-plugin vornehmen:

<plugin>

    <groupId>org.apache.maven.plugins</groupId>

    <artifactId>maven-shade-plugin</artifactId>

    <version>2.4.1</version>

    <executions>

        <execution>

            <phase>package</phase>

            <goals>

                <goal>shade</goal>

            </goals>

            <configuration>

                <relocations>

                    <relocation>

                        <pattern>org.</pattern>

                        <shadedPattern>my.organisation.org.</shadedPattern>

                        <excludes>

                            <exclude>org.sun.*</exclude>

                            <exclude>org.xml.*</exclude>

                            <exclude>org.w3c.*</exclude>

                        </excludes>

                    </relocation>

                </relocations>

                <filters>

                <filter>

                    <artifact>*:*</artifact>

                    <excludes>

                        <exclude>META-INF/*.SF</exclude>

                        <exclude>META-INF/*.DSA</exclude>

                        <exclude>META-INF/*.RSA</exclude>

                    </excludes>

                </filter>

                </filters>

            </configuration>

        </execution>

    </executions>

</plugin>

Vielleicht hilft es ja jemandem weiter...

0 Kudos
jsp
I'm new here

Leider hat es doch nicht gereicht, das Artefakt zu shaden. Insbesondere Dependencies, die viel mit Reflections arbeiten (z.B. Apache Tika) lassen sich so nicht korrekt laden (gerade was den OOXMLParser angeht). Unsere Lösung war die Implementierung eines eigenen Classloaders...