Blog

Verwaltung von Java-Abhängigkeiten für Mendix-Module | CLEVR

Autor
CLEVR
Letzte Aktualisierung
June 26, 2025
veröffentlicht
March 28, 2018

Wenn Sie jemals versucht haben, eine bestimmte Funktion als wiederverwendbares Modul in Mendix zu implementieren, besteht eine gute Chance, dass Sie einige Jar-Abhängigkeiten hinzufügen mussten. Es gibt mehrere Möglichkeiten, dieses Ziel zu erreichen. Aber es gibt eine Methode, die meiner Meinung nach in der Regel am besten funktioniert, da sie mehrere Probleme gleichzeitig behebt.

Aber lassen Sie mich zunächst die bahnbrechenden oder einfach nervigen Probleme bewerten, auf die ich beim Versuch gestoßen bin, ein wiederverwendbares Modul zu implementieren:

  1. Wenn Sie eine JAR-Datei in Ihrem Modul verwenden möchten, z. B. pdfbox v2.3, aber eine andere Version dieser Jar, z. B. v1.8, bereits in einem anderen Modul Ihrer Anwendung verwendet wird, haben Sie Pech gehabt. Soweit ich weiß, ist das einfach nicht möglich. Der Klassenlader nimmt nur eine Version auf, sodass eines der beiden Module die falsche Version verarbeiten muss, was normalerweise dazu führt, dass die Anwendung hängt oder abstürzt. Erschwerend kommt hinzu, dass dies nur auftritt, wenn zur Laufzeit eine bestimmte Java-Aktion mit einer widersprüchlichen Abhängigkeit aufgerufen wird.
  2. Wenn Sie ein Mendix-Modul verwalten und eine darin enthaltene JAR-Datei aktualisieren müssen, müssen Sie sicherstellen, dass alle beim Update die alte JAR-Datei löschen. Andernfalls haben Sie am Ende zwei Jars mit einer anderen Version in Ihrem Classloader-Pfad, was wiederum wahrscheinlich dazu führt, dass die Anwendung nicht funktioniert.
  3. Bei Mendix müssen Sie transitive Abhängigkeiten manuell verwalten. Das bedeutet normalerweise, dass Sie den Code ausführen müssen, um zu sehen, welche Klassen fehlen, dann die richtige JAR-Datei finden und sie manuell zum Projekt hinzufügen müssen. Spülen Sie dann ab und wiederholen Sie den Vorgang, bis keine mehr vorhanden sind.“Keine Klasse gefunden“ Fehler.
  4. Zeitverschwendung beim Exportieren von Modulen ist mein nächstes Ärgernis. Es gibt zu viele Jars, um sie einzuschließen/auszuschließen, insbesondere wenn Sie in Ihrem Projekt etwas wie Community Commons oder das Restmodul verwenden. Natürlich könnte das einfach durch eine Schaltfläche zum Aus-/Abwählen im Dialogfeld „Abhängigkeiten exportieren“ gelöst werden, aber das ist nicht der Sinn dieses Beitrags. Auf die eine oder andere Weise geht Zeit verloren.

Eine Möglichkeit, all diese Probleme effektiv zu lösen, besteht darin, ein Build-Tool zu verwenden, um ein sogenanntes fettes Glas, eine einzelne JAR-Datei, die alle Ihre Abhängigkeiten enthält. Dies allein löst mehrere Probleme aus der vorherigen Liste, darunter: rom Die Fat-JAR-Datei wird automatisch aktualisiert, wenn das Modul aktualisiert wird, wodurch Punkt 2 gelöst wird; das Build-Tool kann sich um alle transitiven Abhängigkeiten kümmern, wodurch Punkt 3 gelöst wird, und schließlich ist die einzige Abhängigkeit eine einzelne Ein-Jar-Datei, wodurch Punkt 4 entfällt.

Das erste Problem, das wir erwähnt haben, kann mit einer Technik namens gelöst werden Abschattung. Shadowing ersetzt Muster in Klassennamen durch eine bestimmte Zeichenfolge. Sie können zum Beispiel ersetzen org.json mit community.commons.org.json. Dadurch kann der Java-Classloader zwei Versionen der org.json-Bibliothek laden, da sie unterschiedliche Klassennamen haben.

Fallstudie: Community Commons

Gibt es ein besseres Modul, um die oben beschriebenen Techniken zu demonstrieren als die Community Commons? In seinem aktuellen Status hat es etwa 20 Abhängigkeiten (sieh dir die Bildlaufleiste an).

In diesem Fall verwende ich Gradle. Sie können sich jedoch dafür entscheiden, dasselbe mit anderen Tools zu tun, z. Maven oder Jarjar Links.

dependencies

Hinzufügen von Abhängigkeiten zu Gradle

Zuerst habe ich das installiert Gradle Eclipse-Plugin nachdem Sie es vom Eclipse-Marktplatz heruntergeladen haben. Als Nächstes habe ich ein neues Gradle-Projekt erstellt. Die Hauptdatei in jedem Gradle-Projekt ist das Gradle-Build-Skript, in dem viele Optionen angegeben werden können, z. B. welche Java-Version verwendet werden soll. Die Abhängigkeiten für ein Gradle-Projekt sind ebenfalls im Build-Skript definiert. Wie Sie unten sehen können, besteht meine erste Iteration des Build-Skripts hauptsächlich aus Standardmaterial, mit Ausnahme des Shadowing-Tools, das ich hinzugefügt habe:

Bauskript {
Repositorien {
MavenCentral ()//suche hier nach Abhängigkeiten
}
Abhängigkeiten {
Klassenpfad „com.github.jengelman.gradle.plugins:shadow:2.0.0"
//das ist das Tool, mit dem wir das Fat Jar bauen und es beschatten werden
}
}

Plugins {
ID 'com.github.johnrengelman.shadow' Version '2.0.0'
ID „java“
}

Gruppe 'com.mendix.community-commons'
Version '1.0.0'

Plugin anwenden: 'Java'
Plugin anwenden: 'Maven'
Plugin anwenden: 'Eclipse'

Task-Wrapper (Typ: Wrapper) {
gradleVersion = '3.0'
}

Kompiliere Java {
Quellkompatibilität = 1.8
TargetCompatibility = 1.8
}

Repositorien {
Maven Lokal ()
Maven Central ()
}

Abhängigkeiten {
}

So weit so gut. Als nächstes fing ich an, das hinzuzufügen Abhängigkeiten aus dem Community-Commons-Modul:

Abhängigkeiten {
//https://mvnrepository.com/artifact/org.owasp.antisamy/antisamy
Gruppe kompilieren: 'org.owasp.antisamy', Name: 'antisamy', Version: '1.5.3'

//https://mvnrepository.com/artifact/com.google.guava/guava
Gruppe kompilieren: 'com.google.guava', Name: 'guava', Version: '14.0.1'

//https://mvnrepository.com/artifact/commons-codec/commons-codec
Gruppe kompilieren: 'Commons-Codec', Name: 'Commons-Codec', Version: '1.10'

//https://mvnrepository.com/artifact/org.apache.pdfbox/jempbox
Gruppe kompilieren: 'org.apache.pdfbox', Name: 'jempbox', Version: '1.8.5'

//https://mvnrepository.com/artifact/joda-time/joda-time
Gruppe kompilieren: 'joda-time', Name: 'joda-time', Version: '2.9.6'

//https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload
Gruppe kompilieren: 'commons-fileupload', Name: 'commons-fileupload', Version: '1.2.1'

//https://mvnrepository.com/artifact/commons-io/commons-io
Gruppe kompilieren: 'commons-io', Name: 'commons-io', Version: '2.3'

//https://mvnrepository.com/artifact/org.apache.commons/commons-lang3
Gruppe kompilieren: 'org.apache.commons', Name: 'commons-lang3', Version: '3.0'

Gruppe kompilieren: 'org.apache.servicemix.bundles', Name: 'org.apache.servicemix.bundles.batik', Version: '1.8_1'

//https://mvnrepository.com/artifact/org.apache.pdfbox/pdfbox
Gruppe kompilieren: 'org.apache.pdfbox', Name: 'pdfbox', Version: '2.0.3'

//https://mvnrepository.com/artifact/xerces/xercesImpl
Gruppe kompilieren: 'xerces', Name: 'xerceSimpl', Version: '2.8.1'
}

Mir ist aufgefallen, dass einige der Abhängigkeiten in Maven Central nicht aufgeführt sind (oder zumindest konnte ich sie nicht finden). Kein Problem — ich habe die JAR-Dateien aus dem Community-Commons-Projekt auf GitHub. Ich habe einen Ordner erstellt Libs in meinem Gradle-Projekt und fügte dann das hinzu com.springsource.org.apache.batik.css-1.7.0.jar und nekohtml.jar dazu. Dann habe ich meinen Abhängigkeiten die folgende Zeile hinzugefügt, die, wie Sie vielleicht erwarten, fügt alle Jar-Dateien aus dem Libs Ordner zum Gradle-Projekt.

kompiliere FileTree (dir: 'libs', einschließlich: '*.jar')

Damit haben wir jetzt alle Java-Abhängigkeiten aufgelöst.

Umgang mit Mendix-Kursen

Meine grundlegende Prämisse ist, die Java-Aktion zu verwenden, um nur eine entsprechende Funktion aus dem von mir erstellten Fat Jar aufzurufen. Dies ist bereits bei den meisten Java-Aktionen in Community Commons der Fall, d. h. der eigentliche Implementierungscode befindet sich nicht in der Java-Aktionsklasse. Stattdessen wird die Ausführung an eine andere Klasse delegiert. Ich habe die Klassen aus dem kopiert Gemeinschaftsgemeinschaften Paket, in dem die Logik implementiert ist, insbesondere ConversationLog, DateTime, ORM, StringUtils usw., für mein Gradle-Projekt. Ich habe das Projekt erstellt und den Build-Pfad in Eclipse aktualisiert. Erfolg... zumindest ein bisschen. Die Abhängigkeiten werden von Eclipse geladen und erkannt, aber ich kann einige fehlende Klassen sehen. Die meisten der fehlenden Klassen stammen aus der Mendix-API, die in den Community-Commons in Form von iContext, ImendixObject, Core usw. ausgiebig verwendet wird. Ich habe sie auch zu den Abhängigkeiten hinzugefügt.

Dateien kompilieren ('C: /Program Files/mendix/7.2.0/runtime/bundles/com.mendix.public-api.jar')
Dateien kompilieren ('C: /Program Files/mendix/7.2.0/runtime/bundles/com.mendix.logging-api.jar')

Ich muss diese Klassen bei der Entwicklung in Eclipse einbeziehen, da der Compiler sie erkennen muss. Andernfalls wird das Projekt nicht kompiliert. Ich möchte jedoch nicht, dass die Mendix-API-Klassen in mein fettes Glas gelangen, also habe ich sie ausgeschlossen.

Schattenglas {
Abhängigkeiten {
schließe 'com/mendix/**' aus
}
}

Neu erstellen und aktualisieren, viele unerkannte Klassen sind jetzt in Ordnung. Hier ist ein Screenshot meines Gradle-Projekts zu diesem Zeitpunkt:

stringutiles

Aber ich sehe immer noch ein paar Importe, die nicht erkannt wurden. Das waren Enumerationen und Proxyklassen aus dem Mendix Modeler, wie System.Proxies.File-Dokument. Ich konnte mir keine Möglichkeit vorstellen, sie zu meiner Liste der Abhängigkeiten hinzuzufügen, also beschloss ich, sie zu verpacken. Ich habe eine Schnittstelle IFileDocument mit dem folgenden Code deklariert:

öffentliche Schnittstelle IFileDocument {
öffentliches ImendixObject getMendixObject ();
öffentlicher boolescher Wert getHasContents ();
}

Ich werde nur mit dieser Schnittstelle innerhalb des Fatjar arbeiten. Dann habe ich eine Methode hinzugefügt, um eine zu konvertieren System.Proxies.File-Dokument zu einem org.community-commons.main.iFileDokument. Jedes Mal, wenn eine Java-Aktion eine Methode aus meiner Fat-Jar-Datei aufrufen muss, die mit Dateien funktioniert, führe ich eine Konvertierung durch und übergebe dann die Schnittstelle.

misc-java

Damit bleiben nur die Aufzählungen und die System.Proxies.Sprache. Für die Aufzählungen z.B. communitycommons.proxies.loglevel, Ich habe eine ähnliche Wrapping-Methode verwendet wie die, die ich für das FileDocument verwendet habe. Für die Sprache Ich habe beschlossen, den gesamten Code (alle vier Zeilen) in der Java-Aktion beizubehalten. Es hat keine anderen externen Abhängigkeiten, warum also die Mühe machen? Bei Bedarf kann es jedoch auf ähnliche Weise verpackt werden. Ich habe einen weiteren Umbau durchgeführt und das Fettglas zum Projekt hinzugefügt. Nachdem alles kompiliert ist, sind dies die Abhängigkeiten, die im Modeler verbleiben.

dependencies-2-1

Lassen Sie mich hier einen kleinen Exkurs machen, um über das Umschließen von Mendix-Proxyklassen zu sprechen. Diese Lösung ist eindeutig nicht das, was Sie normalerweise tun möchten. Andererseits handelt es sich hauptsächlich um mechanische Arbeiten. So wie ich die Dinge sehe, bedeutet das, dass es eine Möglichkeit gibt, es zu automatisieren. Es kann ein Tool entwickelt werden, das Code für die Wrapper-Klassen generiert. Sie könnten sogar noch einen Schritt weiter gehen und das Tool alle Verwendungen von Proxyklassen durch die entsprechenden Wrapper-Klassen ersetzen lassen.

Ressourcen zu einem Fat Jar hinzufügen

Wie Sie auf dem Screenshot sehen können, haben wir es geschafft, die meisten Dateien zu entfernen, mit Ausnahme der Antisamy-XML-Dateien. Ich könnte sie einfach so lassen wie sie sind und alles würde gut funktionieren, aber sie nerven mich wirklich. Der Vollständigkeit halber werde ich zeigen, wie alle Ressourcendateien in das Fat Jar aufgenommen werden können. Aber das ist eindeutig optional.

folder-structure-1

Wie Sie wahrscheinlich bereits wissen, ist eine JAR-Datei nur eine Zip-Datei, was bedeutet, dass wir jede gewünschte Datei darin aufnehmen können. Tatsächlich betrachtet Gradle automatisch alle Dateien, die sich in einem Ordner mit dem Namen resources befinden, als Ressourcendateien und fügt sie dem JAR hinzu. Ich habe einen solchen Ordner erstellt unter src/main und kopierte alle XML-Dateien dorthin. Um zu testen, ob die Ressourcen wirklich hinzugefügt werden, habe ich einen Rebuild durchgeführt und dann die JAR-Datei extrahiert. Ich konnte sehen, dass wirklich alle XML-Dateien im Fat Jar enthalten sind.

Als Nächstes musste ich die Art und Weise ändern, wie die XML-Dateien gelesen werden. Zuvor wurden sie wie normale Dateien aus dem Ressourcenordner geladen. Dies ist jedoch nicht möglich, sobald sie in einer JAR-Datei verpackt sind. Stattdessen solltest du benutze den Classloader, um sie als Ressourcen zu laden. Ich habe die folgenden Zeilen geändert StringUtils.xss bereinigen (was Teil der Community Commons ist):

Dateiname der Zeichenfolge = core.getConfiguration () .getResourcesPath () + file.separator
+ „communitycommons“ + file.separator + „antisamie“
+ file.separator + „antisamy-“ + policyString + „-1.4.4.xml“;
Richtlinie p = Policy.getInstance (Dateiname);

Anstatt einen Dateinamen zu verwenden, verwende ich den Classloader, um den Dateiinhalt als Stream abzurufen:

code-3

Schließlich bleiben uns nur noch drei Abhängigkeiten übrig:

dependencies-3-1
  • Java, das Klassen für die Schnittstelle zum Fat Jar enthält,
  • die Fat-Jar-Datei und
  • txt - eine Lizenzdatei.

Da ich keine Möglichkeit sehe, die Anzahl der Abhängigkeiten weiter zu reduzieren, können wir jetzt zum nächsten Thema übergehen:

Beschattung

Um es einfach zu halten, werde ich einfach das voranstellen cc_ Präfix für alle externen Klassen mit dem folgenden Code:

folder-structure-1-1

Schattenglas {
verschieben ('org.apache', 'cc_org.apache')
verschieben ('org.cyberneko', 'cc_org.cyberneko')
verschieben ('org.joda', 'cc_org.joda')
verschieben ('org.owasp', 'cc_org.owasp')
verschieben ('org.w3c', 'cc_org.w3c')
verschieben ('org.xml', 'cc_org.xml')
verschieben ('javax', 'cc_javax')
verschieben ('java_cup', 'cc_java_cup')
verschieben ('com.google', 'cc_com.google')
Abhängigkeiten {
schließe 'com/mendix/**' aus
}
}

Gradle ersetzt automatisch die passenden Klassennamen sowohl in den Dateien, in denen diese Klassen definiert sind, als auch in den Dateien, in denen diese Klassen verwendet werden.

Beachten Sie, wie ich das weggelassen habe org.community_commons Paket. Diese Klassen beinhalten keine Abhängigkeiten von Drittanbietern, sodass keine Gefahr besteht, dass Konflikte entstehen.

Durch das Öffnen des JAR-Archivs kann ich bestätigen, dass alle Klassen, die aus einer externen Abhängigkeit stammen, tatsächlich umbenannt wurden. Das ist alles was benötigt wird. Jetzt können Entwickler, die Community Commons in ihrem Projekt verwenden, Bibliotheken verwenden, die auch vom Community-Commons-Modul verwendet werden, ohne sich Gedanken über Konflikte machen zu müssen.

code-2-1

Nehmen wir zum Beispiel an, wir wollen die pdfbox-Bibliothek von Apache verwenden, die von Community Commons verwendet wird, auch weil es eine neuere Version mit einigen Funktionen gibt, die uns gefallen, oder weil es eine Komponente gibt, die von einer früheren Version von pdfbox abhängig ist. Das ist jetzt möglich. Wir können das hinzufügen pdfbox.jar in den Ordner userlib und verwende ihn in unserem Code, ohne auf Probleme im Zusammenhang mit Abhängigkeiten zu stoßen.

An dieser Stelle möchte ich betonen, dass es zwar technisch möglich ist, beschattete Klassen in Ihrem Mendix-Projekt zu verwenden, wie in diesem Screenshot gezeigt, dies jedoch um jeden Preis vermieden werden sollte. Es gibt keine Garantie dafür, dass die gespiegelte Klasse nicht entfernt/umbenannt wird und daher nicht mehr verfügbar ist, wenn das Modul aktualisiert wird.

Abschließende Gedanken

Das korrekte Paketieren und Spiegeln von Abhängigkeiten trägt wesentlich dazu bei, Kopfschmerzen bei der Verwendung von Mendix-Modulen zu vermeiden. Ich hoffe, dass diese Beispiele die Entwickler von App Store-Modulen dazu motivieren werden, ihre internen Abhängigkeiten so zu verwalten, dass jeder das Leben ein bisschen einfacher macht. Wenn es genug Interesse und Unterstützung von Seiten der Community gibt, wird der Prozess der Spiegelung von JAR-Dateiabhängigkeiten vielleicht auch Teil eines zukünftigen Best-Practices-Dokuments von Mendix für Module werden — oder, noch besser, wir werden ein integriertes Build/Shadowing-Tool in den Mendix-Modeler integrieren.

Ich hoffe du fandest diesen Beitrag interessant. Wenn Sie Anmerkungen oder Ideen zur Verbesserung dieses Beitrags oder des darin enthaltenen Codes haben, wenden Sie sich bitte an mich. Sie können sich das komplette Projekt auch unter ansehen Github.

Viel Spaß beim Programmieren!

-Andrej Gajduk

Finden Sie heraus, wie CLEVR die Wirkung Ihres Unternehmens steigern kann

Kontaktiere uns

FAQ

Can't find the answer to your question? Just get in touch

No items found.
melde dich für den Newsletter an

Erhalte persönliche Neuigkeiten und Updates in deinem Posteingang

Danke! Deine Einreichung ist eingegangen!
Hoppla! Beim Absenden des Formulars ist etwas schief gelaufen.
CLEVR Company picture Alicia - Ech
No items found.
No items found.