Umstellung von Eclipse Tycho auf bnd / bndtools (OSGi)

Umstellung von Eclipse Tycho auf bnd / bndtools (OSGi)

Die englische Originalversion dieses Beitrags finden Sie hier.

In diesem Beitrag zeigen wir, wie wir unsere Codebase von einem Eclipse Tycho OSGi Build zu einem bnd / bndtools / Gradle Build migriert haben.

Hinweis: Dieser Beitrag ist sehr technisch und richtet sich an Java-Entwickler.

Einleitung

Synesty wurde von Anfang an im Jahr 2010 auf der Basis von OSGi Modultechnologie aufgebaut – eine Entscheidung, die wir nicht bereuen. OSGi passt perfekt zu uns, da jedes Add-On in Synesty (z.B. ein Connector für einen Shop oder ein ERP-System) durch ein OSGi-Bundle dargestellt wird. Add-Ons können von uns, aber auch von Dritten erstellt werden und sie können jederzeit während der Laufzeit kommen und gehen (als Plugins installiert, während die Anwendung noch läuft). Diese Eigenschaft ist einer der großen Vorteile von OSGi.

Seitdem haben wir jedoch bereits zwei grundlegende Änderungen in unserem Build-System vollzogen. 2010 verwendeten wir einen Ant-basierten PDE-Build-Prozess. 2015 wechselten wir zu einem Maven-Build auf Basis von Eclipse Tycho – immer noch in der PDE-Manifest-first-Welt. Das fühlte sich ein bisschen besser an, aber es basierte immer noch auf dem Eclipse Target-Platform-Konzept und P2-Update-Sites. Ende 2019 starteten wir eine neue Migration zu bnd / bndtools mit Hilfe von Data in Motion Consulting. Dank vieler Diskussionen und Bier erzählten sie uns von bndtools, das sie vor einiger Zeit zu verwenden begonnen hatten und das sie nun für fast alle ihre OSGi-basierten Projekte verwenden. Sie sind auch Mitglied der OSGi-Working Group.

Warum jetzt bndtools?

Die Probleme, die wir lösen wollten, waren folgende:

  • zeitaufwendige Einrichtung der Entwicklerumgebung (erforderte eine sehr detaillierte Dokumentation für Entwickler)
  • Eclipse Target-Platform war schwer zu warten
  • OSGi MANIFEST.MF musste manuell gepflegt werden (Manifest-First)
  • Hinzufügen neuer Abhängigkeiten war kompliziert
  • Unterschiede im Build-Prozess in Eclipse vs. Kommandozeile (Maven - Tycho)
  • merkwürdige Effekte beim Wechseln von Git-Branches, die Eclipse verwirrten

Das Einrichten von Entwicklungslaptops oder das Wechseln von Branches oder das Hinzufügen einer neuen Bibliothek (eine .jar-Datei) zu unserer lokalen P2-Update-Site war mühsam. Es erforderte schriftliche Dokumentation, manuelle Änderungen an .xml-Dateien und Neustarts oder Cache-Löschungen, wenn Eclipse die Änderungen nicht erkannte. Wir wünschten, wir könnten einfach neue Bibliotheken hinzufügen, indem wir irgendwo einige Maven-Koordinaten hinzufügen, und fertig.

Sicher, der Grund könnte auch unsere eigene Inkompetenz sein, aber manchmal fühlte es sich wie schwarze Magie an.

Unsere Wünsche und Ziele

  • einfachere Einrichtung der Entwicklerumgebung
  • einfachere Verwaltung von Abhängigkeiten
  • Abhängigkeiten als Maven-Koordinaten angeben (keine Target-Plattform mehr)
  • einfacheres Aktualisieren der verwendeten Technologien auf aktuelle Versionen
  • Verwendung neuerer OSGi-Technologien wie Declarative Services (DS), um Boilerplate-Code zu reduzieren und Annotationen zu verwenden
  • reduzierte Startzeit der Anwendung durch bessere Lazy-Initialisierung (nur Start des Bundles, wenn ein anderes Bundle die Referenz zu verwenden beginnt)
  • konsistenter Build in Eclipse und Kommandozeile

Wie war der Migrationsprozess?

Schmerzhaft :) Ein Unterfangen wie dieses ist, als würde man sein Haus auf einem anderen Fundament wieder aufbauen. Es mussten viele Teile durch neuere Bauteile und Materialien ersetzt werden, während sie sich aber genau gleich verhalten sollten wie vorher. Aber, wir haben bereits viele Kunden mit Tausenden von laufenden Flows, die in diesem Haus leben, die nichts von dieser Änderung bemerken sollten.

Das bedeutet, dass unsere Migrationsstrategie in etwa so aussieht:

  1. Das Ziel war klar: Ein lauffähiges OSGi-System basierend auf bndtools, aber ohne vorhandenen Java-Code zu ändern.
  2. Wir begannen mit einem leeren Projekt und versuchten, es zum Laufen zu bringen.
  3. Dann fügten wir die Bundles (Add-Ons) in ihrer ursprünglichen Form hinzu und passten die bndtools-Konfiguration so an, dass sie zu unserem ursprünglichen Maven-Tycho-System passte.

Für die Entwicklung von 1 und 2 (und schließlich 3) benötigte unser Team neben der normalen täglichen Arbeit etwa 15 Monate.
Der schwierigste Teil war es, die Abhängigkeiten richtig hinzubekommen.

Was ist damit gemeint?
Vorher hatten wir im Grunde einen lokalen Ordner mit .jar-Dateien, der unsere Zielplattform war.
Jetzt wollten wir so viele Abhängigkeiten wie möglich mit Hilfe von Maven-Koordinaten definieren, so dass sie von Maven Central bei Build-Prozess geholt werden. Diese Bibliotheken sollten idealerweise echte OSGI-Bundles sein.

Die meisten der von uns verwendeten Bibliotheken sind bereits OSGi-ready (mit OSGi-Metadaten), ABER leider nicht alle. Daher mussten wir einige dieser Bibliotheken in OSGi-Bundles verpacken / wrappen (manueller Prozess, kann aber mit bnd / bndtools durchgeführt werden).
In einigen seltenen Fällen haben wir die .jar-Dateien noch in das Bundle selbst gepackt, wo wir dachten, dass es den Aufwand nicht wert ist - z.B. einige SDKs von Drittanbietern kommen als ZIP-Datei mit vielen von Abhängigkeiten, die nirgendwo anders benötigt werden.
Da diese Fälle ohnehin in einem separaten Bundle enthalten waren, haben wir die .jar-Dateien auch dort belassen.

Die Hauptprobleme, die wir hatten, betrafen Probleme mit dem Laden von Klassen (die häufigsten Probleme, wenn man über OSGi liest), bei denen eine Bibliothek erwartete, dass eine Klasse von einem bestimmten Classloader geladen wird. Leider treten diese Fehler meist zur Laufzeit oder beim Starten einer Anwendung auf.
Am schwierigsten war es, die Fehlermeldungen im Zusammenhang mit dem Auflösen und dem Laden von Klassen zu verstehen. Manchmal brauchte es eine lächerliche Menge an Neustarts, Googeln, Trial & Error.
Aber um fair zu sein: das Classloading von OSGi - jedes Bundle hat seinen eigenen Classloader - ist eine große Stärke von OSGi. Es erlaubt z.B. die Verwendung verschiedener Versionen der gleichen Bibliothek in Ihrer Anwendung. Etwas, das mit einfachem Java fast unmöglich ist oder zumindest andere Hacks erfordert.

Ein sehr hilfreiches Debugging-Tool auf diesem Weg war der OSGi bnd Snapshot Viewer.

Auch unser Spring- und Hibernate-Stack passt nicht gut in die OSGi-Welt. Im Grunde haben wir beide in einem einzigen zentralen "main-app" Bundle zusammen mit allen Entitätsklassen, die in unserer Anwendung verwendet werden, untergebracht. Das ist ein Teil, der in "unserer" alten Welt genau so gemacht wird wie bisher. Auch die .jar-Dateien für Spring und Hibernate befinden sich aufgrund von Problemen beim Laden der Klassen immer noch lokal im Bundle.
Wir haben dieses Problem noch nicht in Angriff genommen. Vielleicht in der Zukunft, wenn wir auf neuere Versionen aktualisieren.

Im Grunde haben wir also eine Art monolithisches CORE-Bundle mit vielen dynamischen OSGI-Bundles, die außerhalb herumfliegen (unsere Add-Ons).
Der CORE enthält die Artefakte unserer Basis-Applikation (Datenbank, Entitäten, Web-App), während die Add-Ons zusätzliche Konnektoren und Funktionen sind, die auf dem CORE aufbauen. (Update 2023: Mittlerweile scheint es dafür das Buzzword "Modulith" zu geben)

Aber schließlich haben wir es geschafft.

Ja. Es war etwa im Januar 2021, als wir mit einer vollständigen bndtools-Umgebung und gradle build auf allen Servern live gingen. Im Monat davor haben wir den bndtools-Zweig parallel auf einem einzelnen Produktionsserver eingesetzt, um Laufzeitfehler in einer echten Produktionsumgebung zu erkennen. Das bedeutet, dass ein einzelner Server bereits in der neuen Welt lief, während alle anderen Server noch die alte Welt hatten. Da beide auf dem aktuellen Master (alte Welt) basieren, konnten wir so einen Seite-an-Seite-Vergleich durchführen.

Wir fanden einige Laufzeitfehler in einigen Bereichen, die nicht durch automatisierte Testfälle abgedeckt waren. Aber dank unserer Überwachung konnten wir den betroffenen Server schnell stoppen, wenn der Fehler durch die "neue Welt" verursacht wurde, so dass nur ein sehr kleiner Teil der Kunden für eine sehr kurze Zeit betroffen war. Das ist zwar alles andere als optimal, aber für uns war es das geringste Risiko in diesem Big-Bang-Migrationsszenario, das fast jeden Winkel unserer Codebasis berührte.

Seit der Umstellung haben wir damit begonnen, unsere Codebasis noch weiter zu verbessern und neue OSGi-Funktionen zu nutzen.
Hauptsächlich haben wir damit begonnen, einige größere Bundles in kleinere aufzuteilen, API-Bundles einzuführen und mehr und mehr @Component- und @Reference-Annotationen hier und da zu verwenden. Im Grunde haben wir bessere und kleinere Module gebaut und alte Sünden aus der Vergangenheit beseitigt.

Fazit

Was haben wir nach all diesen Mühen gewonnen?

Einfachere Entwicklungseinrichtung

Jedes Teammitglied, das sein Eclipse von der alten Welt auf die neue bndtools Welt umgestellt hat, bestätigte, dass der Setup-Prozess nun viel einfacher ist.

Für ein neues Teammitglied heißt es nun im Wesentlichen:

  • git checkout repo
  • Eclipse herunterladen
  • bndtools Eclipse Plugin installieren
  • Bestehende Hochrechnungen" aus dem Plugins-Ordner des Repo importieren
  • Anwendung mit unserer .bndrun-Datei starten

Verglichen mit der umfangreichen Schritt-für-Schritt-Dokumentation, die wir vorher für die alte Welt hatten, sind diese wenigen neuen Schritte oben sehr minimalistisch.

Auch der Wechsel zwischen Feature-Zweigen bereitet keine Kopfschmerzen mehr.

Das Hinzufügen oder Ändern von Abhängigkeiten ist jetzt ganz einfach

Um eine neue Bibliothek zu ändern oder hinzuzufügen, müssen wir nur noch eine Zeile in unserer
central.maven-Datei, die alle unsere Abhängigkeiten enthält.

Ein Eintrag in dieser Datei sieht wie folgt aus:

org.freemarker:freemarker:2.3.30

was den Maven-Koordinaten in einer pom.xml entspricht wie:

<dependency>
    <groupId>org.freemarker</groupId>
    <artifactId>freemarker</artifactId>
    <version>2.3.30</version>
</dependency>

Früher war dieser Prozess schwieriger und erforderte ein Herumprobieren mit der Eclipse-Zielplattform, um das neue jar zu erkennen. Glücklicherweise kann ich mich nicht mehr an die zahlreichen Schritte erinnern, die dazu nötig sind.

Bessere Module und einfachere Codebase

Das Erstellen neuer Bundles ist jetzt mit dem bndtools Eclipse Plugin einfacher, da es nur ein paar Klicks braucht, um ein neues Bundle zur Codebase hinzuzufügen.
Durch die Verwendung von DS Annotations, um unsere Bundles mit @Component und @Reference zu verdrahten, wurde auch der Boilerplate-Code weniger, da wir Activator-Klassen oder ServiceTrackers loswerden konnten. Das macht es auch einfacher zu lesen und zu pflegen.
Um beispielsweise ein neues Servlet/Controller zu erstellen, musste ein Entwickler früher 3 verschiedene Stellen anfassen, um etwas zu registrieren. Jetzt genügt es, der Klasse einige Anmerkungen hinzuzufügen, und die Registrierung erfolgt automatisch.

Gibt es Nachteile?

Ja. Es ist die längere Erstellungszeit in Eclipse.
Um Ihnen ein Beispiel zu geben:
Wir haben z.B. ein Util-Bundle mit statischen Hilfsklassen, die in fast jedem anderen Bundle verwendet werden. Eine einzige Änderung in diesem Util-Bundle führt also dazu, dass alle anderen Bundles, die davon abhängen, komplett neu gebaut werden müssen.
Dies dauert nun viel länger (zwischen 30-50 Sekunden) und auch das Hot-Code-Reloading funktioniert anders. (Hinweis: Dies ist eine Worst-Case-Situation für einen kleinen Teil der sehr zentralen Klassen. Für die meisten Bündel liegen die Erstellungszeiten bei 1-2s).

Warum?

Erstens: Das Hauptproblem könnten wir selbst sein. Wir könnten unseren Code / unsere Bundles anders strukturieren, um eine solche Situation zu vermeiden.
Aber auf technischer Ebene ist es der Unterschied, wie bndtools und der frühere Eclipse-Compiler arbeiten.
Eclipse PDE basierte früher auf dem ECJ (Eclipse Compiler for Java). Dieser macht irgendeinen inkrementellen Build-Zauber, der Java-Code viel schneller kompiliert. Ich bin mir nicht sicher, was genau er macht, aber in der alten Welt dauerte die Änderung einer zentralen Util-Funktion nur ein paar einstellige Sekunden für den gesamten Workspace-Build, sogar mit Hot-Code-Ersetzung, wenn die Anwendung läuft. Jetzt dauert so etwas leicht 30-50 Sekunden.

Der Grund, warum es jetzt viel langsamer ist, liegt in der Arbeitsweise von bndtools. (...ich sage nicht, dass bndtools langsam ist, es funktioniert nur anders.)
Immer wenn man etwas ändert, prüft bndtools den Abhängigkeitsgraph, welche anderen Bundles davon abhängen.
Und dann baut es zuerst Ihr Bundle und dann alle anderen abhängigen Bundles eines nach dem anderen. Ein Bundle zu bauen bedeutet, javac-Code zu kompilieren, die .jar-Datei zu erstellen, sie in das Repository (lokal) zu installieren, das möglicherweise laufende Bundle zu stoppen und zu deinstallieren, das neue Bundle neu zu installieren und zu starten, falls der Server bereits läuft. Eine Menge (schwerer) Schritte.

Dies ist der korrekte OSGi-Weg, wie die Dinge funktionieren sollten, und die Basis für die coolen Features wie das Bereitstellen von Bundles zur Laufzeit ohne Neustart der Anwendung. Im Grunde genommen wird das Bundle in den laufenden OSGI-Container (in unserem Fall Equinox) neu deployt.
Aber es bedeutet auch, dass die Dinge anders sein werden und man anders denken und programmieren muss.

Wir sind wahrscheinlich noch weit von einem perfekten modularisierten System entfernt und arbeiten immer noch daran, Dinge zu verbessern, aber für die wichtigen Teile profitieren wir bereits sehr von dieser neuen Welt.

Wie haben wir die Erstellungszeiten verkürzt?

Verbesserungen bedeuten oft eine Umstrukturierung von Bundles und Abhängigkeiten durch Aufteilung von Bundles in API- und Implementierungs-Bundles.
z.B. API-Bundles erstellen und die Schnittstellen dorthin verschieben, und sicherstellen, dass andere Bundles nur von den Schnittstellen im API-Bundle abhängen. Da sich die Schnittstellen nicht so oft ändern wie die Implementierungen, verringern sich die langen Kompilierzeiten, da man die Schnittstellen nur selten ändert.
Klingt in der Theorie einfach, ist aber manchmal leichter gesagt als getan, wenn das System in der Produktion läuft.

Im Grunde genommen sind wir also im Moment zufrieden. Für die Bereiche mit häufigen Code-Änderungen haben wir eine ordentliche Aufteilung der Bundles in API-Bundles und Impl-Bundles vorgenommen, so dass die Kompilierzeiten in Eclipse sehr kurz sind (Sekunden). Wir haben immer noch das Problem mit ein paar zentralen Util-Bundles, aber wir haben sie umstrukturiert, so dass Änderungen nicht mehr so häufig vorkommen.

Letzte Worte

Puh, eine Menge Zeug oben...
Diese Technologie-Migration war eine große Anstrengung für uns, aber wir denken, dass es sich gelohnt hat. Wir haben jetzt eine aktuellere und stabilere Plattform und viele Möglichkeiten, neue Tools zu nutzen, die uns auf unserem Weg zur "Anbinden & Automatisieren Ohne Programmieren" (Low Code / No Code) weiterbringen - auch wenn wir viel coden müssen, um das zu erreichen :) Danke an Jürgen von Data in Motion und die Community im bnd / bndtools Forum . Alle waren immer sehr hilfsbereit, wenn wir Fragen oder Probleme hatten. Danke auch an unsere Kunden, denen das alles völlig egal ist und die einfach nichts davon mitbekommen haben... bis zu diesem Artikel ;-)

Unser Whitepaper für Macher: No Code Integration & Automatisierung


Aktualisiert am June 27, 2021
Chatten Sie mit uns