felix_reinhold
Returning Responder

Modul kann nicht auf eigene Klassen zugreifen (scope=module)

Hallo zusammen,

ich habe ein Modul als globale Library. Das Modul stellt Klassen zur Verfügung, die ich in BeanShellSkripten nutzen möchte.
Diese Klassen sind in dem Modul in eine *-public.jar hinterlegt mit dem scope "server". Zusätzlich gibt es für alle weiteren Klassen des Moduls eine *-private.jar mit dem scope "module". Diese enthält bspw. externe Bibliotheken.
Die Skripte möchte ich vom JavaClient und vom WebClient aus aufrufen.

Jedoch werden die Klassen aus dem private.jar nicht gefunden vom Modul.
So sieht etwa meine module.xml aus:

<!DOCTYPE module SYSTEM "../../server/module.dtd">
<module>
      <name>HLP Modul</name>
      <version>1.0</version>
      <description>HLP Modul</description>
      <vendor>HLP Informationsmanagement GmbH</vendor>
      <class>de.hlp.fsmodule.install.CommonsModule</class>
      <components>
           <library>
                <name>HLP Modul SkriptLib</name>
                <resources>
                     <resource>lib/modul-public.jar</resource>
                     <resource scope="module">lib/modul-private.jar</resource>
                </resources>
           </library>
      </components>
</module>

In meinem BeanshellScript erzeuge ich eine neue Instanz einer Klasse aus dem public.jar, bspw. einen WebServiceClient:

WsClient client = WsClientFactory.getClient(settings.getSetting("ps_webservice_runtime").getValue());

Die Klassen WsClient und WsClientFactory liegen beide in der public-jar des Moduls. Sie nutzen dann weitere Bibliotheken, u.a. Jersey. Dort bekomme ich entsprechend folgende Fehlermeldung:
Target exception: java.lang.NoClassDefFoundError: com/sun/jersey/api/client/filter/ClientFilter

Diese Klasse ist im private.jar aber vorhanden. Verstehe ich hier die Funktionsweise der Scopes falsch? Eigtl. sollte doch die Klasse WsClient aus dem jar mit scope "server" auf die Klassen aus dem jar mit dem scope "module" zugreifen können, oder?

Danke und Gruß

Felix

FS 5.0.606

Editiert: XML formatiert

0 Kudos
8 Replies
Peter_Jodeleit
Crownpeak employee

Die Hierachie ist anders herum, Scope "modul" kann auf Klassen aus Scope "server" zugreifen, nicht umgekehrt.
Klassen die in Skripten oder aus anderen Modulen bzw. Scopes benötigt werden, müssen im Scope "server" definiert werden.
Bei einem Service sollte nur die Schnittstelle im Scope "server" definiert werden, Implementierungsklassen sollten im Scope "modul" liegen.

Peter
0 Kudos

Hallo Peter,

vermutlich habe ich mich ein wenig umständlich ausgedrückt. Genau so wie du es geschrieben hast mache ich es auch. Die Klassen die ich im Beanshell-Skript nutze liegen im public.jar mit dem scope "server", alle weiteren Klassen die dann diese Klassen nutzen liegen im private.jar. In meinem Beispiel war das:

Beanshell Skript:

WsClient client = WsClientFactory.getClient(settings.getSetting("ps_webservice_runtime").getValue());

WsClient und WsClientFactory liegen im public.jar

Die WsClient - Klasse könnte bspw. vereinfacht so aussehen:

package de.hlp.firstspirit.ws;

import de.hlp.test.HalloWelt;

public class WsClient  {

  private HalloWelt hw = null;

  public WsClient() {

    hw = new HalloWelt();

  }

}

HalloWelt liegt dann im private.jar, dass eine resource mit dem scope "module" ist.


Da die public.jar und die private.jar beides Ressourcen der gleichen Komponente des gleichen Moduls sind sollte WsClient doch auf HalloWelt zugreifen können (und umgekehrt natürlich nicht), oder?

0 Kudos

Wie ich schrieb, die Klassen aus einem "global" definiertem Scope können nicht auf Klassen aus dem Scope "modul" zugreifen, auch nicht wenn sie aus dem gleichem Modul kommen.

Peter

Ah, sorry. Ich sollte die Antworten mal besser lesen. D.h. ich bräuchte entsprechend wieder ein Executable als Einstiegspunkt?

Dann werde ich nochmal kurz das ursprüngliche Problem formulieren weshalb ich überhaupt auf dieses Thema gekommen bin. Wir verwenden in zwei Modulen die gleiche Library (javax.ws). Bisher hatten wir in Modul 1 nur eine Library mit einer *.jar, die alle Klassen enthält (inkl. javax.ws):

<library>

     <name>Modul1 Lib</name>

     <resources>

          <!-- enthält eigene Klassen und die dazu benötigten Bibliotheken, wie z.B. javax.ws -->

          <resource>lib/modul1-jar-with-dependencies.jar</resource>

     </resources>

</library>

Das Modul 2 besteht aus einer Library und einer Web-App in der zwei jars enthalten:

<components>

     <web-app scopes="project">

          <name>Modul 2 WebApp</name>

          <version>1.0</version>

          <description>Modul 2 WebApp</description>

          <class>de.hlp.allstar.firstspirit.Modul2App</class>

          <web-xml>web.xml</web-xml>

          <web-resources>

               <resource scope="module">lib/modul2-private.jar</resource> <!-- enthält die benötigten Bibliotheken u.a. javax.ws -->

               <resource scope="module">lib/modul2-public.jar</resource> <!-- enthält eigene Klassen -->

          </web-resources>

     </web-app>

     <library>

          <name>Modul2 Lib</name>

          <description>Modul 2</description>

          <resources>

               <resource scope="module">lib/modul2-private.jar</resource> <!-- enthält die benötigten Bibliotheken u.a. javax.ws --> 

               <resource>lib/modul2-public.jar</resource>  <!-- enthält eigene Klassen -->

          </resources>

     </library>

</components>

Nach deiner Erklärung ist mir eigtl. klar, wie das Verhalten dieser Module sein müsste abe rnicht klar, wieso es sich bei uns wie folgt verhält:

Wir haben ein Skript, dass an 3 Stellen ausgeführt wird und eine Klasse erzeugt die in Modul 1 liegt. Diese Klasse nutzt im Hintergrund Klassen von javax.ws.

Das Skript wird im JavaClient über einen Button gestartet und funktioniert.

Das Skript wird im WebClient auf einer Seite per JavaScript über WE_API.Common.execute ausgeführt und funktioniert.

Das Skript wird im WebClient in einem Formular zur Anlage eines Datensatzes per Button gestartet und schmeißt eine ClassLoader-Exception:

Target exception: java.lang.LinkageError: ClassCastException: attempting to

castjar:file:/C:/FirstSpirit5/work/jetty-fs5webedit_269745/webinf/WEB-INF/lib/modul2-private.jar!/javax/ws/rs/ext/RuntimeDelegate.class

to

jar:file:/C:/FirstSpirit5/work/ModuleManager/Modul1_79f4545d/lib/modul1-jar-with-dependencies.jar!/javax/ws/rs/ext/RuntimeDelegate.class

(Habe die Pfade und Namen mal etwas zusammengekürzt).

Das JavaClient und WebClient sich unterscheiden ist für mich klar, da im WebClient das Modul2 in der WebApp installiert ist.

Aber wieso gibt es hier ein unterschiedliches Verhalten zwischen WebClient per JS und WebClient per Formular?

0 Kudos

Hast du die Möglichkeit, das mit einer aktuellen FirstSpirit 5.1 Version zu testen? Oder alternativ in einem Tomcat statt mit dem internem Jetty?

Peter
0 Kudos

Hi Peter,

habe hier jetzt verschiedene Stände.

Auf dem 5.1er System ist es ebenfalls, wie oben beschrieben.

Nun habe ich noch lokal bei mir eine Kopie des Ganzen auf einem 5.0.606er System installiert und hier funktionieren beide WebClient - Variante nicht! Wie wäre denn das richtige Verhalten? Ich kann das dann nochmal probieren mit einem Tomcat nachzuvollziehen. Ich nehme ja mal an, dass Problem kommt daher, dass die Libs der WebApp natürlich unter WEB-INF/libliegen und der WebServer ja nicht "weiß", dass die den scope "module" haben und somit nicht von außen zugreifbar sind, oder?

Ich teste aber nochmal mit dem Tomcat.


Gruß

Felix

0 Kudos

Hallo Felix,

ich habe mir nochmal Gedanken zu dem ClassLoader-Error gemacht. Grundsätzlich sind im Modul-Kontext FAT-Jars eine schlechte Idee, was man auch an dem Error sehen kann (Error != Exception).

FirstSpirit bzw. Java hat hier per ClassLoader keine Chance zu erkennen, dass sich auf dem Class-Path eine doppelte Definition befindet. Dein Modul 1 ist öffentlich und somit auch im Kontext des Modul 2 lesbar. Je nachdem wie die Klassen auflöst werden bzw. wie im Verlauf des Programmablaufs die Klassen auflöst werden, kommt es zum Konflikt. Dabei "weiß" Java nur, dass der sog. Full-Qualified-Name der Klasse identisch ist. Theoretisch könnten das inhaltlich völlig verschiedene Klassen sein. Aus diesem Grund wird ein Error und keine Exception geworfen.

Mögliche Lösungen:

1. Fremdbibliotheken nach Möglichkeit nur Modul-Lokal belassen (Dabei wenn möglich den Originalnamen verwenden, wie dieser z.B. via Maven vergeben wird). Zusätzlich sollte man an den Resource-Tags das Attribut Version setzen (siehe Kaptiel 2.5.1.1 des Modulentwicklerhandbuchs).

2. Wenn man schon eine Fremdbibliothek als Library-Modul in FirstSpirit verwendet, dann darf man in seinen anderen Modulen die gleichen Packages und Java-Klassen nicht mehr mitliefern, vor allem nicht in FAT-Jars. Man sollte dann auch eine Modul-Abhängigkeit eintragen (siehe Kapitel 3.8 des Modulentwicklerhandbuchs), damit der Fall abgefangen wird, dass das Libraray-Modul vor den anderen Modulen deinstalliert wird.

Ich hoffe ich konnte Dir weiterhelfen.

Grüße

Marian Zaplatynski

Hi Marian,

danke für die Antwort und die Tipps.

Dann werden wir wohl bei uns im Produkt jetzt einiges umstellen müssen 😉

Gruß

Felix

0 Kudos