Damit das Flickschustern nicht zu einem unkontrollierten Chaos wird

Veröffentlichen, patchen, updaten

Philip Wenig
Blau-Faden-Roll

Updates sind mittlerweile eine Selbstverständlichkeit. Mal eben Ubuntu und den Browser updaten; ach, der macht das schon von ganz alleine, wie schön. Dass das Updaten in den meisten Fällen so reibungslos funktioniert, ist natürlich kein Zufall. Dahinter steckt eine Menge Arbeit und nicht zu vergessen eine Strategie. Können wir so etwas auch mit Eclipse-Bordmitteln umsetzen. Ja, und das sogar sehr elegant.

Blinder Aktionismus führt in den meisten Fällen zu Chaos – das gilt auch für Updates. Bei kleineren Projekten kann man dieses Chaos noch einigermaßen durch weiteren Aktionismus im Zaum halten. Bei größeren Projekten sieht es da schon schlechter aus. Also muss eine Strategie her, damit die Sache gar nicht erst aus dem Ruder laufen kann. An guten Vorbildern mangelt es ja nicht. Sowohl Mozilla, Chrome und weitere Browser als auch die Betriebssysteme geben hier die Marschrichtung an. Prinzipiell sollte schon beim Anwendungsdesign auf die Fähigkeit zum Aktualisieren geachtet werden. Daher soll mit diesem Artikel näher beleuchtet werden, wie ein entsprechender Updatemechanismus mit Eclipse-Bordmitteln umgesetzt werden kann. Glücklicherweise stehen die Grundzutaten jedem Entwickler zur Verfügung. Daher kommt es nun primär auf die unterschiedlichen Szenarien an, wie eine Anwendung auf dem aktuellen Stand gehalten werden kann.

Strategie

Im Allgemeinen hängt die Strategie zum Aktualisieren der Anwendung natürlich davon ab, welchen Hintergrund die Anwendung besitzt. Ist es eine Rekombination vorhandener Plug-ins, z. B. eine IDE für einen speziellen Einsatzzweck? Ist es ein Open-Source-Projekt oder handelt es sich um eine kommerzielle Anwendung? Ist es gar eine Mischform aus kommerzieller und Open-Source-Anwendung? Des Weiteren ist zu berücksichtigen, ob der Benutzer zusätzliche Plug-ins installieren darf. Wenn ja, wie werden die Plug-ins getestet und zur Verfügung gestellt? Und wie können letztendlich auch die zusätzlich installierten Plug-ins am Updateprozess teilnehmen? Grundsätzlich gibt es dabei unterschiedliche Möglichkeiten (Abb. 1). Eine Erklärung zu den einzelnen Varianten mit entsprechendem Beispiel ist in Tabelle 1 aufgelistet. Eclipse ist eine hervorragende Plattform zur Bereitstellung und Implementierung unterschiedlichster Projekte. Leider kann das Installieren von zusätzlichen Plug-ins einen aber an den Rand der Verzweiflung führen, selbst dann, wenn man schon einige Erfahrung in der Bedienung von Eclipse besitzt. Daher sollten wir es unseren Nutzern bzw. Kunden so einfach wie möglich machen, zusätzliche Funktionalität zu installieren und diese auch aktuell zu halten.

Abb. 1: Art der Eclipse-Anwendung und des verwendeten Updatemechanismus

Abb. 1: Art der Eclipse-Anwendung und des verwendeten Updatemechanismus

Tabelle 1: Auflistung einiger Produkt- und Updatevarianten

Tabelle 1: Auflistung einiger Produkt- und Updatevarianten

Die grundsätzliche Frage beim Updateprozess lautet daher, wie viel Kontrolle man über diesen besitzt bzw. besitzen möchte. Kontrolle und Flexibilität stehen sich hierbei diametral gegenüber. Bei einer Anwendung, die es ermöglicht, Plug-ins aus dem Eclipse-Marktplatz zu installieren, besteht die geringste Kontrollmöglichkeit. Zwar kann die Anwendung über Updates aktualisiert werden, allerdings wird es kaum möglich sein, alle im Eclipse-Marktplatz vorhandenen Plug-ins auf Kompatibilität zur eigenen Anwendung zu testen. Ärger ist dabei vorprogrammiert. Im Gegensatz dazu besitzt der Benutzer dabei aber die größte Freiheit, die Anwendung entsprechend seinen Wünschen anzupassen.

Auf der anderen Seite der Skala befindet sich die vorkonfigurierte Anwendung. Der Benutzer kann zwar nichts zusätzlich installieren, dafür bietet sich ihm aber eine integrierte Komposition aufeinander abgestimmter Plug-ins und Features. Insbesondere im kommerziellen Umfeld ist das eine bevorzugte Variante.

Als Mittelding zwischen beiden Möglichkeiten können Anwendungen mit dem Marktplatz angesehen werden. Das Problem der Kontrollmöglichkeit besteht natürlich nach wie vor im Einbinden des Eclipse-Marktplatzes. Durch die Bereitstellung einer Selektion an Plug-ins, die über einen eigenen Marktplatz installiert werden können, erhält der Benutzer die Möglichkeit, nur die vorselektierten Plug-ins zusätzlich zu installieren. Der eigene Marktplatz erlaubt es, die Plug-ins vor Veröffentlichung auf Kompatibilität und nahtlose Integration in die Anwendung zu testen. Bei einer rekombinierten Anwendung sieht es da schon wieder schwieriger aus, da zusätzlich installierte Plug-ins zu Problemen mit der eigenen Auswahl führen können. Nicht beachtet wurde der Fall, bei dem die Anwendung überhaupt keine Aktualisierungsmöglichkeit besitzt. Im Folgenden gehe ich auf die technische Umsetzung der dargestellten Fälle ein.

Marktplatz und Updateseiten

Prinzipiell funktioniert das Aktualisieren der Anwendung über URLs, die auf spezielle Verzeichnisse zeigen. In den Verzeichnissen liegen die Informationen, die Eclipse zum Updaten der Anwendung benötigt. Die Verzeichnisse können dabei entweder direkt aus der IDE heraus oder besser von einem Build-System wie z. B. Maven/Tycho erstellt werden. Eine Updateseite kann prinzipiell auch lokal gespeichert werden, da es sich nur um eine Sammlung von Dateien handelt. Das unterläuft aber das Prinzip eines dynamischen Updates seitens des Anbieters. Daher gehe ich hier nur auf ein Onlineupdate ein (Abb. 2). Der Marktplatz ist dafür zuständig, die zusätzlich zu installierenden Plug-ins zur Integration in die Anwendung anzumelden. Der Markplatz wird dabei über HTTP/1.1 GET (A) aufgerufen (Listing 1).

http://marketplace.eclipse.org/featured/api/p?product=org.eclipse.epp.package.rcp.product&os=linux&runtime.version=4.3.0.v20130605-2000&client=org.eclipse.epp.mpc.core&java.version=1.7.0_65-b32&product.version=4.3.0.v20130605-2000&ws=gtk&nl=de_DE

Das Ergebnis ist eine XML-Datei (B) (Listing 2), die den Inhalt des Marktplatzdialogs enthält (Abb. 3).

<?xml version=“1.0″ encoding=“UTF-8″?>
<marketplace>
  <featured count=“10″>
  …
    <node id=“29591″ name=“JRebel for Eclipse“ …>
    <updateurl><![CDATA[http://update.zeroturnaround.com/update-site]]></updateurl>
    …

Die Update-URLs für die zu installierenden Plug-ins werden bei der Anwendung hinterlegt (C). Die Anwendung prüft dabei, ob die Dateien artifacts.jar und content.jar unter dem angegebenen URL vorhanden sind (D). Diese enthalten zusätzliche Informationen für den Updateprozess. Im gleichen Verzeichnis gibt es Unterverzeichnisse für die Feature- und Plug-in-Bundles. Handelt es sich um eine Updateseite für eine Anwendung, sind ebenfalls die plattformspezifischen Programmstarter im Unterverzeichnis binary zu finden. Über den Marktplatzdialog können mehrere Marktplätze abgefragt werden. Zu diesen zählt auch der Eclipse-Marktplatz. Das Eintragen einer oder mehrerer Updateseiten kann entweder über den Marktplatzdialog oder auch über den manuellen Weg mittels MENU | HELP | INSTALL NEW SOFTWARE … erreicht werden. Über den Marktplatz ist es halt nur etwas komfortabler.

Abb. 2: Abfrage des Marktplatzes und Installation von Plug-ins über Updateseiten

Abb. 2: Abfrage des Marktplatzes und Installation von Plug-ins über Updateseiten

Abb. 3: Ansicht des Eclipse-Marktplatzdialogs aus Sicht einer RCP-Anwendung

Abb. 3: Ansicht des Eclipse-Marktplatzdialogs aus Sicht einer RCP-Anwendung

Das Ganze funktioniert nach dem gleichen Schema, egal, ob man ein Plug-in manuell installiert, den Eclipse-Marktplatz oder einen eigenen Marktplatz verwendet. Bei der Anwendung werden die zur Installation und zum Aktualisieren nötigen URLs hinterlegt. Ein eigener Marktplatz kann über einen Extension Point definiert werden. Dazu wird ein Eintrag in der Datei plugin.xml hinzugefügt (Listing 1). Die Einträge sollten dabei selbsterklärend sein. Natürlich muss der eigene Marktplatz die GET-Anfragen verstehen und wohldefinierte XML-Dokumente zurückliefern. Der Eintrag selfContained=“false“ bedeutet dabei, dass das Equinox-Updatesystem p2 versucht, benötigte Abhängigkeiten automatisch aufzulösen.

Listing 1

<extension
 point="org.eclipse.epp.mpc.ui.catalog">
 <catalog
    description="Das ist der ACME Marktplatz."
    icon="icons/logo_32x32.png"
    label="ACME Marktplatz"
    url="http://www.acme.com/"
    selfContained="false">
    </catalog>
</extension>

Der Updateprozess muss daher also wissen, wo weitere Updateseiten zu finden sind, um zusätzlich benötigte Abhängigkeiten auflösen zu können. Man kann die Seiten in den Einstellungen manuell hinzufügen. Aber das sollten wir dem Benutzer nicht antun. Es gibt eine elegantere Variante. Beim Programmstart können per Default Updateseiten gesetzt werden (Listing 2). Der Benutzer muss sich darum also nicht mehr kümmern. Hier zeigt sich auch die Stärke einer eigenen Kompilation mit eigenem Marktplatz. Die Abhängigkeiten können sehr genau kontrolliert werden. Das gleiche Prinzip wird angewandt, wenn die Anwendung genau nur eine definierte Updateseite besitzen soll. Das ist von besonderem Interesse, wenn es sich um kommerzielle Anwendungen handelt, die eine exakte Kontrolle über den Updateprozess besitzen müssen. Im Prinzip kann über die dargestellte Vorgehensweise eine entsprechende Updateseite hinzugefügt werden. Das Problem ist natürlich nur, dass die kommerzielle Anwendung Aktualisierungen nur von dieser und von keinen zusätzlichen Seiten beziehen soll. Das kann dadurch gewährleistet werden, dass beim Programmstart bereits vorhandene Updateseiten entfernt werden (Listing 3) und der produktspezifische URL erst danach wieder hinzugefügt wird. Das funktioniert für den Fall (D) der Abbildung 1 sehr gut. Allerdings sollte man diese Vorgehensweise nicht bei den anderen Fällen (A) bis (D) der Abbildung 1 verwenden, denn dann könnten Probleme bei der Installation von zusätzlichen Plug-ins über den Marktplatz/die Marktplätze auftreten. Entfernt man die URLs aller vorhandenen Updateseiten, sind zwar die über den Marktplatz installierten Plug-ins weiterhin in der Anwendung lauffähig, das Aktualisieren funktioniert dann aber nicht mehr. Außerdem würde ein Plug-in im Marktplatzdialog weiterhin zur Installation angeboten werden, obwohl es bereits installiert ist. Das wollen wir nicht.

Abb. 4: Vorhandene Softwareseiten im Einstellungsdialog einer RCP-Anwendung

Abb. 4: Vorhandene Softwareseiten im Einstellungsdialog einer RCP-Anwendung

Listing 2

public void addDefaultProvisioningRepositories() {
  Map<String, String> updateSites = new HashMap<String, String>();
  updateSites.put("Kepler", "http://download.eclipse.org/releases/kepler");
  updateSites.put("The Eclipse Project Updates", "http://download.eclipse.org/eclipse/updates/4.3");
  updateSites.put("Eclipse Orbit", "http://download.eclipse.org/tools/orbit/downloads/drops/R20130517111416/repository");
  updateSites.put("e4 Tools", "http://download.eclipse.org/e4/downloads/drops/I20130624-2200/repository");
  addProvisioningRepositories(updateSites);
}

public void addProvisioningRepositories(Map<String, String> updateSites) {
  ProvisioningSession session = ProvisioningUI.getDefaultUI().getSession();
  RepositoryTracker repositoryTracker = ProvisioningUI.getDefaultUI().getRepositoryTracker();
  URI[] locations = repositoryTracker.getKnownRepositories(session);
  for(Map.Entry<String, String> updateSite : updateSites.entrySet()) {
    if(updateSiteNeedsToBeAdded(locations, updateSite.getValue())) {
      addUpdateSite(updateSite, session, repositoryTracker);
    }
  }
}

private boolean updateSiteNeedsToBeAdded(URI[] locations, String updateSite) {
  for(URI location : locations) {
    if(updateSite.equals(getCleanURL(location))) {
      return false;
    }
  }
  return true;
}

private String getCleanURL(URI uri) {
  String url = uri.toString();
  if(url.endsWith("/")) {
    url = url.substring(0, url.length());
  }
  return url;
}

private void addUpdateSite(Map.Entry<String, String> updateSite, ProvisioningSession session, RepositoryTracker repositoryTracker) {
  try {
    URI location = new URI(updateSite.getValue());
    repositoryTracker.addRepository(location, updateSite.getKey(), session);
  } catch(URISyntaxException e) {
    logger.warn(e);
  }
}

Listing 3

ProvisioningSession session = ProvisioningUI.getDefaultUI().getSession();
RepositoryTracker repositoryTracker = ProvisioningUI.getDefaultUI().getRepositoryTracker();
URI[] locationsEnabled = repositoryTracker.getKnownRepositories(session);
repositoryTracker.removeRepositories(locationsEnabled, session);
IMetadataRepositoryManager metadataRepositoryManager = (IMetadataRepositoryManager)session.getProvisioningAgent().getService(IMetadataRepositoryManager.SERVICE_NAME);
URI[] locationsDisabled = metadataRepositoryManager.getKnownRepositories(IRepositoryManager.REPOSITORIES_DISABLED | repositoryTracker.getMetadataRepositoryFlags());
repositoryTracker.removeRepositories(locationsDisabled, session);

Updates, Tests und Upgrades

Über den Einstellungsdialog (Abb. 4) kann der Ort der Updateseiten manuell verändert werden. Das ist im Grunde genommen nicht erwünscht, da sich der Benutzer nicht um die Details der Updateseiten kümmern sollte. Im Rahmen des Einsatzes der Listings 4 und 5 würden die Updateseiten beim Anwendungsstart sowieso zurückgesetzt werden. Ein manuelles Editieren der URLs im Einstellungsdialog ist manchmal aber trotzdem erwünscht. Darüber kann nämlich ein Testsystem aufgebaut beziehungsweise eine Teststrategie umgesetzt werden (Abb. 5). Für Tests wird ein Verzeichnis mit dem Namen repository-stage vorgehalten.

http://updates.acme.com/0.9.x/repository-stage
  binary
  features
  plugins
  artifacts.jar
  content.jar

Dieses enthält die gleiche Dateistruktur wie die offizielle repository-Updateseite. Wurde eine neue Version der Software erstellt, so kann die kompilierte Updateseite entweder manuell oder automatisch durch das Build-System in das entsprechende Testverzeichnis übertragen werden. Nach einem Setzen des repository-stage-URL im Einstellungsdialog kann das Update nun getestet werden. Das kann ebenfalls entweder manuell, semiautomatisch oder automatisiert erfolgen. Ein automatisiertes Testen des GUI mithilfe von Jubula wäre hier sicher sehr interessant. Erst wenn das Testen erfolgreich war, werden die Updates allen Benutzern zur Verfügung gestellt. Auf diese Weise wird sichergestellt, dass der Benutzer nicht mit kryptischen Fehlermeldungen konfrontiert wird.

Abb. 5: Struktur von Updates und Upgrades sowie entsprechenden Test-Repositories

Abb. 5: Struktur von Updates und Upgrades sowie entsprechenden Test-Repositories

Diese Vorgehensweise (Abb. 5) ermöglicht aber nicht nur das Testen und Bereitstellen von regelmäßigen Updates (A), sondern auch, bestehende Anwendungen auf die nächste Version zu aktualisieren (B). Hierzu wird der URL der hinterlegten Updateseite auf die nächste Version gesetzt. Dabei sollte ebenfalls nach der beschriebenen Teststrategie vorgegangen werden. Nach einem erfolgreichen Upgrade startet die Anwendung dann in der nächsten Version.

Produktvarianten

Wie soll nun vorgegangen werden, wenn verschiedene Produktvarianten ausgeliefert werden sollen? Im Grunde lässt sich auch das relativ einfach umsetzen. Jedes Produkt muss hierzu eine eigene Produktdefinition *.product besitzen. Darüber kann eine Anwendung (Tabelle 1) zum Beispiel in einer Open Source Community Edition (C) und in einer Enterprise Edition (D) bereitgestellt werden. Die Enterprise Edition kann dabei zusätzliche Plug-ins enthalten, die in der Community Edition nicht enthalten sind, oder nur über den Marktplatz nachinstalliert werden können. Hierbei ist es wichtig, dass die enthaltene Produktfunktionalität über Features eingebunden wird. Damit kann eine einheitliche und saubere Featurevererbungshierachie umgesetzt werden. Features sind ohnehin die Grundlage für einen sauberen Updateprozess sowie für eine ordentliche Auszeichnung der benötigten Abhängigkeiten. In den produktspezifischen Plug-ins kann nun das Setzen der Updateseiten (Listing 2 und 3) und eines eventuell benötigten Marktplatzes (Listing 1) vorgenommen werden. Damit ist es möglich, unterschiedliche Produkte und getrennte Updates (Abb. 6 und 7) auf Grundlage einer gemeinsamen Codebasis umzusetzen.

Abb. 6: Updateseiten für die Basisanwendung und die Plug-ins der Community Edition

Abb. 6: Updateseiten für die Basisanwendung und die Plug-ins der Community Edition

Abb. 7: Updateseiten für die Basisanwendung der Enterprise Edition

Abb. 7: Updateseiten für die Basisanwendung der Enterprise Edition

Fazit

Das Aktualisieren eigener Anwendungen ist nicht schwierig. Eclipse stellt hierzu die benötigte Funktionalität zur Verfügung. Ein der Zeit angemessenes Aktualisierungssystem kann also mit Eclipse-Bordmitteln umgesetzt werden. Gedanken sollte man sich allerdings bezüglich der Strategie machen. Hierbei stehen sich die Flexibilität der Anwendung und eine Kontrolle über den Aktualisierungsprozess grundsätzlich gegenüber. Je nach strategischer Ausrichtung der Anwendung können beide Anforderungen gut miteinander kombiniert werden. Prinzipiell ist es sinnvoll, sich bereits vor der Umsetzung einer Anwendung Gedanken über die Aktualisierungsstrategie zu machen. Es ist aber auch nachträglich möglich, eine Strategie für Aktualisierungen zu implementieren. Damit steht dem Aktualisieren eigener Eclipse-Anwendungen nichts mehr im Weg.

Verwandte Themen:

Geschrieben von
Philip Wenig
Philip Wenig
Dr. Philip Wenig ist Gründer der Open-Source-Software OpenChrom (http://www.openchrom.net) und Geschäftsführer der Lablicate UG (http://lablicate.com). Er beschäftigt sich hauptsächlich mit der Entwicklung von Software für den Bereich der Chromatographie und Massenspektrometrie (GC/MS). Darüber hinaus beteiligt sich seine Firma Lablicate UG aktiv an der Science Working Group (WG) der Eclipse Foundation (http://science.eclipse.org).
Kommentare

Hinterlasse einen Kommentar

avatar
4000
  Subscribe  
Benachrichtige mich zu: