Wertesemantik bei Login-IDs

Seit einiger Zeit erlebe ich wieder häufiger, dass sich Administratoren kleiner IT-Umgebungen den Kopf über die Namenskonventionen bei der Vergabe von Login-IDs zerbrechen. Eine besonders beliebte Praxis sind Login-IDs die sich aus Vorname und Nachname des Benutzers bzw. aus Teilen des Namens oder Geburtstags zusammensetzen. Als massgeblicher Vorteil wird ins Feld geführt, dass Benutzer sich ihren Login-Name dann besser merken könnten und damit die Anmeldung vereinfacht wird.

Allerdings ergeben sich damit zahlreiche Probleme:

  • Bei statistisch häufiger auftretenden Namen wie Max Müller hat man recht schnell ein Problem, denn max.mueller darf nicht doppelt vergeben werden. Kurzerhand wird mit max.mueller1, max.mueller2 usw. eine x-beliebige ID ins Spiel gebracht, die das Namenskonzept über den Haufen wirft und für den Benutzer nicht plausibel ist: „Warum hat der eine Max die 1 und ich die 2? Bin ich die Nummer 2 hier im Geschäft?“
  • Besonders hässlich wird die Sache, wenn Max Müller im System A die Login-ID max.mueller2 hat und im System B die Login-ID max.mueller22. „Welche Login-ID brauche ich für was? Warum bin ich hier die 2 und dort die 22?“
  • Noch viel häufiger ist der Fall, dass Benutzer ihre Namen wegen Heirat ändern, dann passt der Name nicht mehr. Und nicht selten bestehen Anwender bei Namensänderung nach Scheidung zu Recht auf die Änderung der Login-ID, denn in vielen Anwendungen wird diese angezeigt, z. B. in Änderungshistorien von Anwendungen. Wird der Name aber dann tatsächlich geändert, ist das ID-Konzept plötzlich dahin. Besonders wenn die ID fest in archivierten Daten und Dokumenten verdrahtet ist, die sich nicht mehr so ohne Weiteres ändern lassen, lässt sich nicht mehr auf den eigentlichen Benutzer schließen, der im Dokument erwähnt wird. Das kann zu einem riesigen Problem werden, wenn nicht mehr klar ist, welcher Anwender dann tatsächlich hinter einer ID steckte, wenn Max Mustermann zwischenzeitlich Max Müller heißt und ein zweiter Max Mustermann ins Unternehmen einsteigt.
  • Es ließen sich noch mehr Nachteile aufführen, die alle den grundsätzlichen Charakter von IDs und deren Verknüpfung zur Entity – in unserem Fall zur Entity „Person“ – betreffen.

Bewährt hat sich bei Login-IDs folgendes:

  • Verwenden Sie grundsätzlich keine Namenskonvention, sondern eine x-beliebige ID. Nummerieren Sie beispielsweise alle Benutzerkonten stur durch oder nehmen Sie einen generierten String aus Buchstaben und Zahlen. Jeder Anwender, der seine ID mehrere Wochen auf seinem Rechner eingetippert hat, kann sie im Schlaf aufsagen wie seinen Geburtstag.
  • Verwenden Sie eine möglichst kurze ID. Viele Systeme beschränken die Login-ID auf eine bestimmte Länge. Anwender, die nicht mit 10-Fingern schreiben, werden es Ihnen danken.
  • Verwenden Sie aus dem selben Grund für alle IDs immer die gleiche Länge.

Nun gibt es das obige Problem, dass der Anwender schwer zwischen den Benutzerkonten aus unterschiedlichen Benutzerdatenbanken unterscheiden kann, wenn es in der IT-Umgebung mehrere Benutzerdatenbanken gibt. Trotz Active Directory, LDAP und SSO ist es nicht immer möglich, ein und dieselbe Benutzer-ID in allen Systemen zu verwenden und dann kann Wertesemantik durchaus Sinn machen. (Ich spreche ausdrücklich von „kann Sinn machen“, denn am Ende werden wir sehen, dass auch diese Semantik zu den gleichen Problemen führt).

Um das Problem von max.mueller2 und max.mueller22 in Systemumgebung A bzw. Systemumgebung B zu umschiffen, kann man dem Benutzer etwas Wertsemantik auf den Weg geben. Sinnvoll ist es, dem Anwender einen Präfix als Hilfe zu geben z. B. AD für Benutzerkonten aus dem Active Directory oder LD für Benutzerkonten aus dem LDAP oder z. B. S1 für Benutzerkonten aus der Benutzerdatenbank 1.

Damit ergeben sich folgende Login-IDs, die sich prima an den Anwender kommunzieren lassen und die er sich prima merken kann:

  • AD-DIF22X3 für das Active-Directory-Benutzerkonto.
  • LD-DIF22X3 für das LDAP-Benutzerkonto.
  • S1-DIF22X3 für das Benutzerkonto aus Benutzerdatenbank 1.

Vorallem in sehr heterogenen IT-Umgebungen ist das für den Anwender ein Segen, wenn er nicht mit unterschiedlichen technischen Begriffen – wie Active Directory, LDAP, lokales oder Domain-Benutzerkonto – konfrontiert wird, sondern wenn ihm einfach mitgeteilt wird: „Bitte verwenden Sie ihr S1-Konto für die Anmeldung an System X und ihr LD-Konto für die Anmeldung an System Y.“ Das ist für den Benutzer plausibel, genauso wie er in der großen weiten Internet-Welt maxi@spassvoegel.de für die Anmeldung an Chat-Foren verwendet und max.mueller22@gmx.de bei Amazon.

Sie ahnen es aber bereits: Mehr als 10 weitere Benutzerdatenbanken sollten Sie im obigen Fall nicht haben, dann ist der ID-Pool erschöpft. Sieht man aber davon ab, dass selbst in großen IT-Landschaften mehr als 10 Benutzerdatenbanken eher selten sind, kann diese Konvention für Anwender und Administratoren durchaus vorteilhaft sein.

Im Zweifelsfall: Vergessen Sie’s! – IDs mit Wertsemantik machen im wahrsten Sinne des Wortes einfach keinen Sinn.

Veröffentlicht in Sonstiges. Leave a Comment »

Java-Applets und Java-Web-Applications in Multi-Module-Projekten referenzieren

Java-Applets sind mittlerweile recht selten geworden. Dennoch gibt es einige Anwendungsfälle, bei denen ein Applet unter Umständen in eine Web-Applikation eingebunden werden muss. Insbesondere dann, wenn eine Legacy-Anwendung mit Java-Swing umgesetzt wurde und aus mehreren externen Libraries besteht, möchte man die Anwendung nicht zwangsläufig neu erfinden und stattdessen mit einem Applet auf dem kürzesten Weg ins Web bringen.

Kompliziert wird dieses Unterfangen ganz besonders dann, wenn mit Maven gebaut werden soll und die Abhängigkeiten der Anwendungsteile innerhalb großer und komplexer Projekte berücksichtigt werden sollen.

Wir greifen dazu folgendes Beispielprojekt auf, das aus drei Teilen besteht:

de.example.component (Multi-Module-Project)
+---de.example.app (jar - Anwendungscode mit Main bzw. Applet-Klasse)
+---de.example.core (jar - Business-Logik)
\---de.example.web (war - Web-Anwendung)

Die Web-Anwendung bindet auf einer ihrer HTML-Seiten die Applet-Klasse bzw. den Anwendungscode wie üblich folgendermaßen ein:

			<applet code="de.example.app.AppletMain.class" archive="applet/de.example.app.jar,applet/de.example.core.jar,applet/com.external.lib.jar" width="100%" height="100">
			</applet>

Das App-Module „de.example.app“ mit den eigentlichen Appletsourcen verwendet das Core-Module „de.example.core“. Darüberhinaus ist denkbar, dass die einzelnen Module externe Libraries referenzieren, die an den richtigen Stellen in die Anwendung verbaut werden müssen. In unserem Beispiel soll das durch die externe Library „com.external.lib“ veranschaulicht werden.

Das POM der Module „Core“ und „App“ als auch das Multi-Module-POM sollten für den geübten Maven-Anwender leicht umzusetzen sein.

Schwierig wird es beim POM der Web-Anwendung. Weil das Applet vor der Web-Anwendung gebaut werden muss, jedoch vor dem Applet erst das Core-Module zu bauen ist, müssen wir das App-Module so in den Dependency-Tree unseres Multi-Module-Projekts einhängen, dass die Build-Reihenfolge Core—App—Web eingehalten wird.

Dazu definieren wir die folgende Abhängigkeit im POM des Web-Moduls, wobei ${project.version} in unserem Fall durch die Version des Multi-Module-Projekts ersetzt wird:

		<dependency>
			<groupId>de.example</groupId>
			<artifactId>de.example.app</artifactId>
			<version>${project.version}</version>
			<scope>provided</scope>
		</dependency>

Diese Abhängigkeit sorgt allerdings nur dafür, dass unser Applet vor der eigentlichen Web-Anwendung gebaut wird. Leider wird das aus dem Core-Module resultierende JAR nicht in das Web-Archive paketiert. Gleiches gilt auch für die vom Applet benötigten Libraries; auch diese fehlen im WAR, sodass beim Aufruf der HTML-Seite die JVM ins Leere greift und uns mit Fehlermeldungen bestraft.

Eine Änderung des Scope-Attributs würde das Problem übrigens nicht beseitigen. Es ist bewusst so gewählt. Warum wir den Scope ausgerechnet auf „provided“ setzen müssen, werden wir sogleich erfahren.

Die Ursache des Problems liegt nämlich darin verborgen, dass ein Applet nichts anderes als eine einfache HTML-Ressource ist und dementsprechend auch als HTML-Ressource in das WAR eingebunden werden muss. Der von uns verwendete Dependency-Mechanismus berücksichtigt jedoch nur Dependencies, die als Libraries von der Web-Anwendung direkt benötigt und im WEB-INF/lib-Verzeichnis der Web-Anwendung abgelegt werden, auf die lediglich der Web-Container des Servers Zugriff hat, der Web-Browser des Anwenders hingegen jedoch nicht.

Kurz gesagt, die Einbindung der Libraries als Dependencies der eigentlichen Web-Anwendung würde uns nicht weiterhelfen, im Gegenteil, sie ist sogar überflüssig und würde das WEB-INF/lib-Verzeichnis mit überflüssigen Libraries überfüllen. Mit dem Scope „provided“ sorgen wir genau dafür, dass die Libraries nicht über den Dependencies-Mechanismus des Web-Archivs kopiert werden.

Was heißt das im Detail?

Wie für HTML-Ressourcen üblich, muss auch unser Applet inklusive der Libraries „de.example.app.jar“ „de.example.core.jar“ und „com.external.lib.jar“ so im WAR abgelegt werden, dass ein Browser diese Ressourcen vom Server anfordern kann. Prinzipiell bietet sich dazu jeder beliebige Unterordner des WAR an, außer eben WEB-INF und META-INF, wie wir eben festgestellt haben. Wir werden die Libraries dazu im Unterordner „applets“ ablegen. Wie bereits oben im HTML-Ausschnitt zu sehen ist, haben wir das Unterverzeichnis im APPLET-Tag schon verwendet.

Wie bekommen wir die ganzen abhängigen Libraries in das applet-Verzeichnis?

Sicherlich könnte man die Libraries per Hand in das applet-Verzeichnis kopieren. Bei Code-Änderungen müssten wir allerdings die Libraries erneut dorthin kopieren, was spätestens bei größeren Projekten mit vielen externen Libraries sehr viel Aufwand verursachen würde und zudem sehr fehleranfällig wäre.

Die Lösung zum Kopieren abhängiger Libraries liegt im Maven-Dependency-Plugin. Mit diesem Plugin kann man nämlich sämtliche Abhängigkeiten einsammeln und an ein beliebiges Ziel kopieren. Dazu ergänzen wir im POM des Web-Moduls folgende Plugin-Konfiguration:

			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-dependency-plugin</artifactId>
				<version>2.4</version>
				<executions>
					<execution>
						<id>copy-dependencies</id>
						<phase>prepare-package</phase>
						<goals>
							<goal>copy-dependencies</goal>
						</goals>
						<configuration>
							<outputDirectory>${project.build.directory}/${project.build.finalName}/applet</outputDirectory>
							<stripVersion>true</stripVersion>
							<includeScope>compile</includeScope>
							<artifactItems>
								<artifactItem>
									<groupId>de.example</groupId>
									<artifactId>de.example.app</artifactId>
									<type>jar</type>
									<overWrite>true</overWrite>
								</artifactItem>
							</artifactItems>
						</configuration>
					</execution>
				</executions>
			</plugin>

Mit der Konfiguration sorgen wir dafür, dass vor der eigentlichen Paketierung des WARs, und zwar in der Maven-Phase „prepare-package“ das copy-depencies-Goal des Maven-Dependency-Plugins ausgeführt wird (Zeile 8 und 10). Wichtig dabei ist, dass als ID-Attribute „copy-dependencies“ gewählt wird, sodass die default-Execution überschrieben wird (Zeile 7).

Als Output-Directory geben wir das Unterverzeichnis „applet“ des WAR-Buildfolders an (Zeile 13). Mit stripVersion können wir die Version-Suffixe an den JARs beim Kopieren entfernen lassen (Zeile 15), sodass wir das Applet-Tag im HTML nicht korrigieren müssen, wenn sich die Versionsnummer unserer Anwendung oder die einer referenzierten Library irgendwann einmal ändern sollten.

Als <ArtifactItem> muss lediglich unser Projekt mit dem Applet angeben werden. Das Maven-Dependency-Plugin sorgt dafür, dass alle von „de.example.app“ ausgehenden Abhängigkeiten kopiert werden.

Ein Aufruf von mvn clean package auf das Multi-Module-Projekt liefert uns nun endlich ein frisches WAR mit dem von uns gewünschten Inhalt:

de.example.web.jar (Multi-Module-Project)
|   index.html
|
+---applet
|       com.external.lib.jar
|       de.example.app.jar
|       de.example.core.jar
|
+---META-INF
\---WEB-INF

Nach dem Deployment des WARs und dem Aufruf der entsprechenden Web-Seite im Browser sollte sich das Applet präsentieren.

Ein besonderes Schmankerl ist allerdings noch offen…

In Eclipse lassen sich Web-Projekte sehr komfortabel mit dem Kommando „Run As -> Run on Server“ starten. Für einen solchen Klick-and-Run-Test ist die obige Konfiguration allerdings nicht geeignet, weil Eclipse für den Build und das Deployment der Web-Anwendung auf einen Webserver einen anderen Mechanismus verwendet.

Sicherlich könnte man auch hier wieder die benötigten Libraries in den WebContent- oder webapp-Ordner des Projekts kopieren, was aber sehr fehlerträchtig ist und zu Problemen mit dem Maven-Build führen würde.

Viel besser ist es, dass Eclipse-Projekt so zu konfigurieren, dass die Libraries vor dem Start des Servers von Eclipse kopiert werden. Dazu öffnet man die Project-Properties des Projekts „de.example.web“ und passt unter „Deployment Assembly“ die Packaging Structure des „Web“-Projekts an, indem man mit „Add“ folgende Assembly-Mappings hinzufügt:

Add of "Folder"                    src/main/java                    WEB-INF/classes
Add of "Folder"                    src/main/webapp                  /
Add of "Archive from File System"  C:/mvnrepo/com/external/lib[..]  applet/
Add of "Project"                   de.example.core                  applet/de.example.core.jar
Add of "Project"                   de.example.app                   applet/de.example.app.jar

Die beiden letzten Einträge sind insofern notwendig, dass sowohl das Core-Projekt als auch das App-Projekt jeweils als Project Reference in das Web-Projekt eingehangen werden, sodass Eclipse unser Web-Projekt immer mit einem Applet auf Basis des aktuellsten Sourcecode-Standes versorgt und auf dem Test-Server bereitstellt.

Anschließend lässt sich das Web-Projekt auch aus Eclipse auf einen beliebigen Server deployen und sofort im Browser starten.

Use parts of filenames of numbered files to rename files in another directory

Sometimes you want to rename a bunch of files by using parts of the filenames of some other files. A very often case is, that you have a bunch of pictures, where you have added a human readable suffix for better classification.

PIC0001_alice-at-work.JPG
PIC0002_bob-at-home.JPG
PIC0003_harry-in-holidays.JPG
PIC0004_susan-at-shopping.JPG

Unfortunately you also have another folder were you have some corresponding files. For example you have put a copy of the original files into a second folder. A similar case is, that you have the original files in JPG-Format and for publishing you converted the files to PNG.

\copy
  PIC0001.PNG
  PIC0002.PNG
  PIC0003.PNG
  PIC0004.PNG

Now a very common task is to rename all files in the second folder „copy“ in the same way so that „PIC0001.PNG“ becomes „PIC0001-alice-at-work.PNG“ like your Files in the original JPG-Folder. A very anoying task if you have to rename all files by hand. And maybe you already tried to put this task onto the commandline or into a batch script, but you stumbled around the usage of filenames and variables.

Because this isn’t a really trivial task I wrote the following batch script to overcome this renaming problem on Windows machines:

@ECHO OFF
SETLOCAL EnableDelayedExpansion
FOR %%F IN (PIC*.JPG) DO (
	SET filename_src=%%F
	SET number=!filename_src:~3,4!
	FOR %%I IN (*!number!*.*) DO (
		SET filename_out=%%~nI
		ECHO !number!
		ECHO Renaming !filename_out!
		RENAME .\copy\PIC!number!.png !filename_out!.png
	)
)

Put this code into a file named „conveyer.cmd“ and change the corresponding lines for your customization needs. Sure, this code needs some explanation:

Line 2:
This statement causes each variable to be expanded during script execution. Unfortunately all variables are expanded at parse time by default, especially the temporary number of the inner loop. So we need to call „SETLOCAL EnableDelayedExpansion“ to get the number immediately at runtime. Moreover to expand a variable immediately you need to enclose it with an exclamation mark (!).

Line 3:
At this line you can put your own filename pattern, to select all files for renaming.

Line 5:
At this line we extract the number of the file. At the example above the number is found at position 3 and has a length of 4 characters. Keep in mind to enclose the full variable with an exclamation mark.

Line 9:
To finally rename the file you need to customize the filepattern that catches the file to be renamed. In the example above the files to rename reside at the subfolder „copy“ of the files that filenames we are use for renaming.

Finally put this script into the original folder and call „conveyer.cmd“. With some more little changes you can further customize this lines for your special needs. For myself I use this script to get the names of sent tiff-fax-documents and to rename the corresponding compressed pdf-documents for archiving.

Missing Option ab Excel 2007: Excel-Diagramme an Fenstergröße anpassen

Benutzer, die Exceldiagramme häufig auf unterschiedlich großen Bildschirmen betrachten, vermissen in Excel ab Version 2007 häufig die Option „Fenstergröße angepasst“, die es bis Excel 2003 noch gab. Mit dieser Option konnte man sicherstellen, dass die Diagrammfläche dynamisch an die jeweilige Fenstergröße angepasst wurde. Mit der Einführung von Excel 2007 wurde diese Option leider gestrichen, kann aber mit den folgenden Schritten in ähnlicher Form reaktiviert werden.

Öffnen Sie dazu im betreffenden Arbeitsblatt das Ribbon „Ansicht“. Dort wählen Sie „Zoommodus:Auswahl“. Wichtig ist, dass das Arbeitsblatt nun als XLSX abgespeichert wird. Schließen Sie das gespeicherte Arbeitsblatt und öffnen Sie es erneut. Wenn Sie nun die Größe des Fensters verändern, wird die Größe des Diagramms wieder automatisch angepasst.

Bei Excel 2010 ist diese Funktion leider an einer anderen Stelle versteckt. Dort wählt man im Ribbon „Ansicht“ die Schaltfläche „Zoom“. In dem sich öffnenden Dialog wählt man die Option „An Markierung anpassen“ und speichert das Arbeitsblatt. Anschließend passt sich die Größe des Diagramms ebenso automatisch an das Fensters an.

Speichert man das Arbeitsblatt nicht als XLSX sondern als XLS und öffnet das Arbeitsblatt erneut, wird das Diagramm nicht flächenfüllend dargestellt, sodass man wieder scrollen oder zoomen muss.

Veröffentlicht in Sonstiges. Schlagwörter: , . Leave a Comment »

Wicket + Maven + Eclipse: „Can not determine Markup“

Vor einigen Tagen begegnete mir beim Ausprobieren einer Wicket-Applikation folgende Exception:

org.apache.wicket.markup.MarkupNotFoundException: Can not determine Markup. Component is not yet connected to a parent. [Page class = de.shylynx.wicket.playground.pages.HomePage, id = 0, render count = 1]

Das Merkwürdige an diesem Verhalten war, dass die Anwendung bereits einwandfrei lief und scheinbar plötzlich Ihren Dienst versagte. Eine genauere Recherche im Netz führte mich dann jedoch auf eine Spur zur endgültigen Ursache des Problems.

Bekanntermaßen bestehen Wicket-Applikationen immer aus einer WebPage-Definition und einer HTML-Seite, die das Markup enthält, z. B. HomePage.java und HomePage.html. Gewöhnlicherweise werden beide Source-Files immer im gleichen Package des Source-Folders src/main/java abgelegt, sodass man beide Dateien leicht zur Hand hat. Das Tückische daran ist, dass mein Eclipse-Projekt so konfiguriert war, dass Eclipse beim Build nur *.java-Files aus dem Source-Folder berücksichtigte und die *.html-Files eben nicht.

Um das Problem zu beheben hangelt man sich zu „Project Properties > Java Build Path > Source“. Dort entfernt man beim Source-Folder src/main/java das Include-Pattern **/*.java. Alternativ kann man dort natürlich auch **/*.html als Include-Pattern hinzufügen. Der Effekt ist der gleiche. Nach einem Redeploy und Neustart des Servers sollte die Anwendung wie gewünscht starten.

Wie kam das Include-Pattern dorthin?
Das Projekt hatte ich ursprünglich mit Eclipse erstellt und erst danach für Maven aufbereitet. In dieser Zeit war das Include-Pattern des Source-Folders leer. Später habe ich im POM des Projekts einige Libraries hinzugefügt. Um die Libraries im Projekt verwenden zu können, habe ich mit mvn eclipse:eclipse das Eclipse-Projekt neu generiert. Unter Verwendung der Standardkonfiguration generiert Maven die Classpath-Entries für den Source- und Resource-Folder allerdings mit den oben genannten Include- und Exclude-Patterns.

Um html-Files zu inkludieren, muss das Maven-Eclipse-Plugin entsprechend konfiguriert werden. Wie das geht ist in der Dokumentation des Maven-Eclipse-Plugin zu finden.

Mixed Maven-Multi-Module-Projects mit OSGi: Tycho vs. Apache-Felix-Bundle-Plugin

Multi-Module-Projekte vereinen immer unterschiedliche Softwaretechniken, die durch die wohl bekannte Layer-Architektur heutiger Anwendungen bedingt sind. Dabei wird die Anwendung in der Regel in die Module Core, Services, Persistence und UI aufgeteilt. Sehr häufig wird dabei ein und dieselbe Anwendung für mehrere Clients (z. B. GUI, Commandline und Web-Client) entwickelt. Das Projekt wird dann zusätzlich in die einzelnen GUI-Projekte web, cli und rcp aufgeteilt. Für Client-GUIs hat sich dabei Eclipse-RCP bewährt.

RCP-Client-Anwendungen werden im Gegensatz zu Webanwendungen häufig mit dem Ziel entwickelt, Fachlogik vom Server auf die Seite des Clients zu verlagern, insbesondere dann, wenn die Kommunikation mit dem Server aus Performancegründen unterbunden werden soll oder wenn Fachlogik offline bereitgestellt werden soll. Dann steht man spätestens beim Build und Deploy der Module jedoch vor dem Problem, die klassische Java-Welt mit der OSGi-Welt von Eclipse zu integrieren. Weil für die Entwicklung von RCP-Anwendungen alle Module als Plugin bzw. OSGi-Bundle bereitgestellt werden müssen, ist man mit der schwierigen Entscheidung konfrontiert, die einzelnen Module wie Core, Services und unter Umständen auch den Persistence-Layer in OSGi-Bundles zu gießen und diese letztendlich auch effizient zu bauen, zu paketieren und bereitzustellen.

Hier gibt es zwei Ansätze:
Werden die Anwendungen vollständig als OSGi-Anwendung entwickelt, sollte man auf jeden Fall einen Blick auf Maven Tycho werfen. Diese Variante kann ich für den obigen Fall zur Zeit allerdings noch nicht ganz uneingeschränkt empfehlen, weil Maven Tycho bei gemischten Multi-Module-Projekten noch einige Stolpersteine bereithält, die die Migration von bestehenden Java-Projekten nicht so einfach machen.

Werden allerdings lediglich Core und Service als OSGi-Bundle gewünscht, hat sich der folgende Ansatz bewährt.
Dabei werden Core- und Service-Layer weiterhin als klassische Maven-Java-Projekte entwickelt. Allerdings müssen nun die Manifest-Dateien der JARs um die fehlenden OSGi-Bundle-Einträge ergänzt werden. Diese Aufgabe überträgt man am besten an das Apache-Felix-Maven-Plugin. Es generiert die gewünschten OSGi-Bundle-Einträge für das endgültige JAR. Anschließend werden die so generierten Bundles in die Targetplatform des Zielprojekts deployt, sodass Core und Services zu guter Letzt als OSGi-Bundles in den RCP-Projekten verwendet werden können.

Auf diese Weise werden Risiken bei der Mavenisierung von gemischten RCP/OSGi-Builds erheblich reduziert.

Integrationstest in den Maven-Build-Lifecycle einbinden

Maven bietet mit der Phase „integration-test“ die leichte Einbindung von Integrationstests an. Der erste Gedanke zur Einbindung von Integrationstests ist häufig, dass man das Surefire-Plugin neben der Phase „test“ einfach um ein Weiteres an die Phase „integration-test“ bindet. Das Surefire-Plugin hat allerdings den Nachteil, dass die weitere Ausführung des Maven-Build-Prozesses bei einem Fehler sofort abgebrochen wird. Häufig ist es aber im Fall fehlschlagender Integrationstests notwendig die Infrastruktur wieder in einem wohl definierten Zustand zu verlassen, also beispielsweise einen Testserver zu stoppen oder Datenbanken zu bereinigen.

Die Lösung für dieses Problem bietet das Maven Failsafe Plugin. Mit dem Plugin wird der Buildprozess im Gegensatz zum Maven Surefire Plugin auch im Fehlerfall fortgeführt. Nachfolgende Build-Phasen, insbesondere die Phase „post-integration-test“, die etwa für die Bereinigung der Testinfrastruktur benutzt werden soll, können also trotz fehlgeschlagener Integrationstests ausgeführt werden.

Um allerdings einerseits normale Unittests in der Phase „test“ auszuführen und im Fehlerfall den Buildprozess abzubrechen, andererseits aber auch Integrationstests auszuführen und im Fehlerfall den Buildprozess mit der Phase „post-integration-test“ fortzuführen, sind einige weitere Überlegungen notwendig.

Wie sind das Surefire-Plugin und das Failsafe-Plugin gemeinsam für Integrationstests zu konfigurieren?
Grundsätzlich sind beide Plugins zunächst so zu konfigurieren, dass bei der Testausführung mit dem Surefire-Plugin alle Integrationstests während der Phase „test“ ausgeschlossen werden und umgekehrt in der Phase „integration-test“ durch das Failsafe-Plugin ausschließlich Integrationstests ausgeführt werden.

Die Konfiguration sieht dann folgendermaßen aus:

	<build>
		<plugins>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-surefire-plugin</artifactId>
				<version>2.12</version>
				<executions>
					<execution>
						<id>default-test</id>
						<phase>test</phase>
						<goals>
							<goal>test</goal>
						</goals>
						<configuration>
							<excludes>
								<exclude>**/*IntegrationTest.java</exclude>
							</excludes>
						</configuration>
					</execution>
				</executions>
			</plugin>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-failsafe-plugin</artifactId>
				<version>2.12</version>
				<executions>
					<execution>
						<goals>
							<goal>integration-test</goal>
							<goal>verify</goal>
						</goals>
						<configuration>
							<includes>
								<include>**/*IntegrationTest.java</include>
							</includes>
						</configuration>
					</execution>
				</executions>
			</plugin>
		</plugins>
	</build>

Wichtig bei der Konfiguration ist, dass die Testexecution der Surefire-Konfiguration die ID „default-test“ trägt. Damit wird die Standardexecution des Surefire-Plugins überschrieben, weil diese nämlich sämtliche Testklassen erfasst, die dem folgenden Muster entsprechen:

  • **/Test*.java
  • **/*Test.java
  • **/*TestCase.java

Folglich würden in der Phase „test“ auch Tests erfasst und ausgeführt, die auf *IntegrationTest.java enden. Um das zu verhindern, werden stattdessen mit dem oben gewählten Pattern grundsätzlich alle Tests erfasst und nur Integrationstests in der Test-Phase ausgeschlossen.

In der Integrationstest-Phase werden die Integrationstest mit dem gleichen Pattern inkludiert.