Eclipse Projekt für ein eigenes Modul

Aus iDempiere de
Zur Navigation springen Zur Suche springen

Wer die Installation der Entwicklungsumgebung hinter sich und iDempiere aus dieser heraus laufen hat, wird sich nun als nächstes fragen, wie man denn nun damit etwas entwickeln kann. Neben der Weiterentwicklung des Kerns werden die meisten Anwender hier vor allem eigene Geschäftslogik für die Warenwirtschaft implementieren wollen. Wir fangen wir das nun an?

Einführung

iDempiere besteht aus vielen einzelnen OSGI-Bundles, die als Eclipse-Projekte eingerichtet sind. Dadurch ist die Software stark modularisiert. Von den vorhandenen Bundles werden jeweils diejenigen zusammen geladen, die für einen bestimmten Job nötig sind - so gibt es eine Gruppe von Bundles, die zusammen den iDempiere-Server darstellen und eine andere Gruppe, die den Swing-Client darstellen. Man kann nun einzelne Bundles hinzufügen oder weglassen; es wäre z.B. denkbar, den Server mit oder ohne die ZK-Web-Oberfläche zu starten.

Wollen wir nun eigene Geschäftslogik implementieren, so bietet es sich an, diese in ein eigenes Eclipse-Projekt zu bringen, aus dem wir ein eigenes OSGI-Bundle erzeugen. Nun müssen wir noch eine Startkonfiguration erzeugen, die den iDempiere-Client und -Server jeweils so startet, das unser eigenes Bundle mitgeladen wird.

Die Kommunikation zwischen Bundles geschieht über sogenannte OSGI Extension Points. Diese stellen die öffentlichen Schnittstellen der Bundles dar.

Ein letzter Punkt ist die Erzeugung und Verteilung eines fertigen Softwarepaketes. Dabei ist es wünschenswert, sowohl ein Paket für die gesamte Software mit allen benötigten Bundles erzeugen zu können als auch ein Paket nur von unserem Bundle zu haben, das wir während unserer Entwicklungszyklen schnell in einen vorhandenen Server hineinsetzen können (das sollte OSGI eigentlich können).

Wie mache ich mich schlau?

Wer diese Einführung nicht versteht oder mehr zu den angesprochenen Themen wissen will, sollte folgende Linkliste durcharbeiten. Das sind die Quellen, aus denen ich meine Informationen habe:

Erzeugung des Projektes

Nachdem der Eclipe Workspace mit den ganzen iDempiere Projekten gefüllt ist, erzeuge ich ein neues - eigenes - Projekt mit "File -> New Project". Aus den Wizards wähle ich "Plug-in Project" aus. Dort gebe ich einen Projektnamen an (z.B. "FreiBier") und wähle als Target Platform "OSGI standard". Wichtig ist, das ich auf der ersten Seite "Use default location" deaktiviere und einen eigenen Dateipfad angebe. Dort wähle ich einen Pfad, der außerhalb des Workspace liegt. Würde ich mein Projekt innerhalb des iDempiere Workspace erzeugen, so würde Mercurial davon ausgehen, das ich es in das entsprechende Repository hochladen möchte. Ich könnte also weder angenehm neue Versionen von iDempiere herunterladen noch mein eigenes Modul in einem anderen Repository verwalten. Als ID gebe ich eine vollqualifizierte ID der Form "de.bayen.freibier.base" an, so das meine ID immer weltweit einzigartig bleibt. Als Name reicht dann "freibier". Als Target-Plattform wählt man am besten OSGi-Standard, da wir nicht planen, exotische Equinox-Fähigketien zu nutzen. Wer möchte, kann gerne eine Aktivator-Klasse erzeugen. Diese bietet Einstiegspunkte, die aufgerufen werden, wenn das Bundle geladen wird. Schaden kann das nicht.

Konfiguration des Bundle über MANIFEST.MF

Die Konfiguration unseres neuen Bundles geschieht einerseits in der Datei META-INF/MANIFEST.MF und andererseits in der Datei plugin.xml (die erst bei Bedarf erzeugt wird). Um zu sehen, was man einstellen kann, sollte man beherzt mit einem Doppelklick die Manifest-Datei öffnen. Der sich öffnende Editor erlaubt den Zugang auf mehrere Seiten, die man über die Register-Tabs am unteren Rand auswählen kann.

Auf der ersten Seite (Overview) kann man auswählen "This plug-in is a singleton". Das bewirkt, das unser Bundle immer nur genau einmal geladen wird. Von dieser Registerkarte aus sind alle weiteren Konfigurationsmöglichkeiten aus sichbar und/oder durch Links erreichbar.

ManifestOverview.png

Auf der zweiten Seite (Dependencies) kann man nun links auswählen, von welchen anderen Bundles dieses unser Bundle abhängt. Nun sollte man eigentlich schon wissen, was wir mit unserem neuen Modul eigentlich wollen. Wollen wir ein Callout erzeugen, sollte dort org.adempiere.base.callout ausgewählt werden (TODO: Ist das wirklich nötig?), generell brauchen wir org.adempiere.base. Auf der rechten Seite wählt man nun die Java Packages aus, die unser Paket importieren und benutzen darf. Um mit Callouts zu arbeiten, wählt man dort z.B. org.adempiere.base und org.compiere.model aus. Welche Packages genau man braucht, ergibt sich jedoch während der Arbeit, so das wir hier auch einstweilen gar nichts eintragen können. Falls wir ein Paket benutzen, das wir nicht importiert haben, gibt die Eclipse Umgebung uns eine Warnung und erlaubt uns dann, den Eintrag in der Situation vorzunehmen, wenn er das erste Mal gebraucht wird.

Auf der dritten Seite (Runtime) kann man Pakete angeben, die man selbst exportiert. Ich bin mir (bisher) nicht ganz sicher, ob das nötig ist, um damit OSGI Extensions zu exportieren und lasse das erstmal weg.

OSGI Extension Points nutzen

Auf der "Overview"-Seite gibt es rechts einen Link zur Seite (Extensions). Diese existiert bisher noch nicht und wird erst mit dem Klick auf diesen Link erzeugt. (Genau genommen wird die Datei plugin.xlm erzeugt und in dieser ein Extension-Abschnitt erzeugt.) Mit dem "Add"-Button können wir einen OSGI Extension Point heraussuchen, in den wir unseren eigenen Code einhängen wollen. Letztlich ist das ja der Sinn unseres ganzen Bundles. Als Beispiel kann man hier org.adempiere.base.IColumnCallout wählen. Über diesen Extension Point kann man Callouts implementieren. Hat man einen solchen Extension Point eingefügt, kann man (Achtung: Trick!) mit einem Rechtsklick auf diesen und den Menüpnukt "new" eine Extension erzeugen. Hat man das gemacht, kann man rechts die Parameter dieser Extension eingeben (z.B. die Klasse, die als Callout aufgerufen wird und die Tabelle und Spalte, für die das Callout aufgerufen wird).

Manifest-Extensions.png

die erste eigene Klasse

Als Test wollen wir einen einfachen Callout schreiben, der nur eine Ausgabe erzeugt um zu beweisen, das er aufgerufen (und damit erfolgreich installiert) wurde. Ich erzeuge hierzu eine Klasse de.bayen.freibier.base.TestCallout.java. Diese implementiert das Interface org.adempiere.base.IColumnCallout. Dieses wird von Eclipse akzeptiert, da ich das entsprechende Paket vorher im Manifest als "Imported" ausgewählt hatte. Die ganze Klasse sieht nun so aus:

  package de.bayen.freibier.base;
  
  import java.util.Properties;
  
  public class TestCallout implements IColumnCallout {
	@Override
	public String start(Properties ctx, int WindowNo, GridTab mTab,
			GridField mField, Object value, Object oldValue) {
		System.out.println("TestCallout aufgerufen: " + mTab.getName() + "/"
				+ mField.getColumnName());
		return null;
	}
  }


Erstellen einer Extension

Diese eigene Klasse wollen wir nun als Extension in iDempiere einbinden. Dazu gehen wir wieder auf den Manifest-Editor (Doppelklick auf die manifest.mf oder die plugin.xml Datei) und rufen die Seite (Extensions) auf (wie oben beschrieben ggf. das erste Mal über die Overview-Seite, falls noch keine angezeigt wird). Dort wählen wir mit den "Add"-Button die Extension org.adempiere.base.IColumnCallout aus (bzw. haben das vorher bereits getan). Mit einem Rechtsklick auf diesen Eintrag und den "new..." Menü ersteleln wir nun eine neue Extension. Auf der rechten Seite gibt es Einträge für class (da wählen wir unsere Testklasse de.bayen.freibier.base.TestCallout aus), tableName (da wählen wir zum Testen z.B. "AD_Table") und columnName (da nehmen wir "Description"). Um diesen Extension Point nutzen zu können, muss man wissen, das hier die Datenbanktabelle sowie die Datenbankspalte mit passender Gross- und Kleinschreibung eingetragen werden müssen.

Erzeugen einer Launch Configuration

Eine Launch Configuration in Eclipse enthält alle Informationen, die Eclipse braucht, um eine Applikation zu starten. Im Falle einer OSGI Applikation ist das gar nicht so trivial, weil man im Gegensatz zu einer normalen Applikation, die einen Classpath und eine Startklasse hat, einiges mehr (und fremdes) einstellen kann. Zum Glück gibt es aber bereits eine fertige Launch-Konfiguration, die wir modifizieren können. Wir öffnen also mit dem Menü "Run -> Debug Configurations..." das Fenster, um die Konfigurationen zu bearbeiten. Wir wählen links unter dem Baum "Eclipse Application" den Eintrag swingclient.product aus. Über die obere Toolbar oder das Kontextmenü können wir diesen nun duplizieren.

In dem erzeugten Duplikat ändern wir nun die folgenden Einträge:

  • Name: habe ich auf einen eigenen Namen geändert, z.B.: swingclient.product.freibier
  • Plug-ins: auf dieser Registerkarte wähle ich mein eigenes OSGI-Bundle zusätzlich aus
  • Common auf dieser Seite ändere ich den Pfad in "shared file" auf den Pfad meines eigenen Eclipse-Projektes (also z.B. freibier). Hier wird die entsprechende Launch-Datei abgespeichert (und kann dann auch mit im Versionskontrollsystem gespeichert und weitergegeben werden)

LaunchConfigurationFreiBier.png

Testen

Nun startet man die Launch Configuration (z.B. mit dem "Debug" Button unten rechts im Fenster). Man loggt sich in iDempiere als Systemadministrator ein (z.B. System/System) und kann dann im iDempiere Hauptmenü unter "Application Dictionary -> Table and Column" die Ansicht der Tabellen öffnen. Dort ändert man nun eine beliebige Beschreibung ("Description"). IN der Eclipse-Konsole sollte sofort nach verlassen des Eingabefeldes die entsprechende Meldung unseres selbsterstellten Bundles erscheinen! :-)

weitere Themen

Erweiterung von *Factory Services

Für *Factory extension points hat mir Heng Sin empfohlen, keine Equinox Extensions, sondern OSGi Services zu benutzen. DAs sagt mir erst einmal nicht viel, bedeutet allerdings, das die vermeintliche Extension, um z.B. eine IDocFactory als Erweiterung zu definieren, nicht wie oben angebenen als Extension in Eclipse in der MANIFEST.MF definiert wird. Stattdessen wird (wie z.B. im OSGI Wiki beschrieben) eine xml-Datei in "/OSGI-INF/*.xml" erzeugt. Diese wird dann im Wert "Service-Component:" in der MANIFEST.MF angegeben. Beispiele hierzu findet man z.B. im Paket "org.adempiere.base".

Prinzipiell funktioniert dieser Mechanismus insofern anders als die Extensions, als ein Service mit einem hohen "service.ranking"-Wert den Standard-Service verdrängt. Der Extension-Mechanismus hingegen erzeugt mehrere Factorys, die dann ggf. hintereinander aufgerufen werden. Leider wird die Default-Factory zuerst aufgerufen, was es schwer macht, Ihre Funktion zu ersetzen. Diese Punkte sollten von jemandem näher dokumentiert werden, der mal mit mehreren Factories gleichzeitig gearbeitet hat.