Maven 3.5 - Torsten Horn

Werbung
Maven 3.5
+ maven.apache.org
+ Maven-Multimodulprojekte
+ andere TechDocs
+
Apache Maven ist (ähnlich wie Ant und Gradle) ein leistungsfähiges Werkzeug, um
viele in der Softwareentwicklung immer wieder anfallende Prozeduren zu
automatisieren und zu vereinfachen. Es wird manchmal als "Build Management System"
bezeichnet und ist Teil vom "Software Configuration Management (SCM)".
Während Ant eher kommandoorientiert arbeitet, ist Maven eher strategisch orientiert,
realisiert mehr Abstraktionen, wird deklarativer gesteuert, berücksichtigt Abhängigkeiten
besser und ist besonders für aufwändigere Multimodulprojekte geeignet.
Im Folgenden wird nur auf Maven 3.5 eingegangen. Informationen zu Vorgängerversionen
finden Sie unter Maven 3.3.9, Maven 2.2.1 und Maven 1.
Inhalt
1. Vergleich mit Ant
2. Einige wichtige Begriffe zu Maven
3. Installation von Maven
4. Maven-Hello-World-Projekt
5. Ausführbare Jar-Datei
6. Ausführbare Jar-Datei inklusive Abhängigkeiten mit dem Assembly Plugin
7. Maven-Webapp-Projekt mit Jetty
8. Maven-Webapp mit Properties im Manifest
9. Maven-Properties-Projekt mit Ressourcen-Filterung
10. Erweiterung um Maven-Profile
11. Generierung von HTML, PDF, RTF etc. mit DocBook und dem Docbkx Maven
Plugin
12. Java-Codegenerierung aus einem XSD-Schema mit JAXB
13. REST-Webservice mit JAX-RS und SOAP-Webservice mit JAX-WS
14. FTP-Client und embedded FTP-Server im JUnit-Test
15. Einfaches Multimodulprojekt
16. Multimodulprojekt mit Plugin-Management und Dependency-Management
17. Multimodulprojekt mit Corporate POM
18. Site Report um Project Reports zur Sourcecodeanalyse erweitern
Vorbemerkungen zu Site Project Reports, Site Project Reports mit Maven 3.5
19. Sourcecodeanalyse mit SonarQube
20. Erstellung von Javadoc- und Source-Archiven
21. Signaturen erzeugen
22. Java-Programme (und andere Programme) mit dem exec-maven-plugin ausführen
23. Guice und AOP
24. Eigenes Maven-Plugin (Mojo)
Mojo mit Parameter, Mojo-PluginContext, Mojo-JUnit-Test
25. Test-Jar
26. JUnit-Tests mit JUnit 5
27. Parallelisierte Testausführung mit TestNG
28. JMockit
29. Automatisierter Integrationstest mit Jetty, HtmlUnit und HttpUnit
30. Automatisierter Integrationstest mit Jetty und JWebUnit
31. Automatisierter Integrationstest mit Selenium
32. Automatisierter Integrationstest mit Fit
33. Automatisierter HTML-Akzeptanztest mit Jetty und Fit
34. Automatisierter Integrationstest mit Cargo für WebLogic, JBoss, WildFly und
Tomcat
35. Integrationstests mit dem Maven Failsafe Plugin
36. Webapp mit Wicket
37. Vert.x-HelloWorld
38. OSGi-Bundle mit dem Maven-Bundle-Plugin
39. Suche mit Lucene
40. Java-EE-Anwendungen (Servlet, JSP, JSF, JPA, EJB3)
41. Maven mit Docker
42. Maven-Repository
Zugang zu Internet-Repositories über Firmen-Proxy, Team-Repository über
freigegebenes Verzeichnis, Repository-Manager, Installation und Konfiguration des
Repository-Managers Archiva, Distribution-Deployment mit dem Repository-Manager
Archiva, Repository-Manager Nexus
43. Continuous Integration mit Jenkins / Hudson und Maven 3
Vergleich mit Ant
Sowohl Ant als auch Maven sind leistungsfähige Werkzeuge, um viele in der
Softwareentwicklung immer wieder anfallende Prozeduren zu automatisieren und zu
vereinfachen. Beide haben ihre bevorzugten Einsatzbereiche.
Vorteile von Ant:




Ant ist leichter einzurichten und zu erlernen
Für kleine einfache Projekte kann Ant einfacher zu handhaben sein
Meistens reicht eine einzige Steuerdatei ('build.xml'), mit der über verschiedene
'Targets' unterschiedliche Ergebnisse erzeugt werden können
Für Ant gibt es in vielen Entwicklungsumgebungen (z.B. Eclipse) gute Unterstützung
Vorteile von Maven:







Fördert Standardisierungen, 'Convention over Configuration' und die Realisierung von
'Best Practices'
Fördert Wiederverwendung, einheitliche Verzeichnisstrukturen und einheitliche
Organisation der Abhängigkeiten
Vereinfacht das Handling bei vielen Abhängigkeiten und benötigten Zusatz-Artefakten
Vereinfacht die Verwaltung von Multimodulprojekten
Fördert durch die Definition der Goals in Plug-ins die Arbeitsteilung zwischen
Konfigurationsmanagement und Softwareentwicklung
Erzeugt nicht nur Javadoc, sondern auch weitere hilfreiche Dokumentationen
Bietet Unterstützung und Anbindung für weitere Anwendungen (Fehlerverfolgung,
Reporting-Systeme, Integrationssysteme)
Einige wichtige Begriffe zu Maven
Maven
Maven ist ein Tool für das Projektmanagement von Softwareentwicklungsprojekten.
Dabei werden Patterns und Best Practices eingesetzt, um die Produktivität und
Wiederverwendbarkeit zu erhöhen. Im Fokus stehen Builds, Dokumentation, Reporting,
Abhängigkeiten, SCM (Software Configuration Management), Releases und Distribution.
Siehe hierzu auch: What is Maven?
Doku zu Maven
Doku zu Maven finden Sie zum Beispiel unter:
http://maven.apache.org/guides
http://maven.apache.org/guides/getting-started
http://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html
http://maven.apache.org/plugins
maven-definitive-guide_de.pdf
Buch: Martin Spiller, Maven 2
Buch: Kai Uwe Bachmann, Maven 2
Build Lifecycle
Maven beinhaltet als wichtiges Konzept genau definierte Build Lifecycles.
Die drei Standard-Lifecycle sind: clean, default und site.
Build Lifecycles bestehen aus Lifecycle-Phasen.
Lifecycle-Phasen
Build Lifecycles sind unterteilt in Phasen.
Wichtige Phasen sind beispielsweise: clean, compile, test, package, integration-test,
verify, install, deploy und site.
Die Phasen werden in einer bestimmten Reihenfolge durchlaufen. Wird im
Kommandozeilenfenster zum Beispiel das Kommando "mvn package" eingegeben, dann
werden alle vorhergehenden Phasen (z.B. u.a. compile und test) und die angegebene
Phase (hier package) ausgeführt, aber nicht die nachfolgenden Phasen.
In den Lifecycle-Phasen werden jeweils bestimmte Plugin-Goals ausgeführt.
Maven-Plugin
Maven-Plugins sind Bibliotheken, die thematisch zusammengehörende Goals
implementieren.
Wichtige Plugins sind zum Beispiel: archetype, compiler, surefire, jar, war,
install, deploy, site und dependency.
Eine Liste vieler weiterer Maven-Plugins finden Sie hier.
Hinweise zur Plugin-Konfiguration finden Sie hier.
Goal
Goals sind von Maven-Plugins angebotene ausführbare Kommandos.
Goals können bestimmten Lifecycle-Phasen zugeordnet werden und werden dann
automatisch zum richtigen Zeitpunkt aufgerufen (z.B. "compiler:compile").
Viele Goals können auch direkt über die Kommandozeile aufgerufen werden. Dabei wird
getrennt durch einen Doppelpunkt (":") der Plugin-Name und der Goal-Name angegeben,
zum Beispiel so: "mvn archetype:generate".
Maven-Goals sind vergleichbar mit Ant-Tasks.
Archetype und Standard Directory Layout
Ein Maven-Archetype ist ein Projekt-Template. Über das archetype-Plugin können
Standard-Directory-Layouts und Projektvorlagen für verschiedene Projekttypen erstellt
werden (z.B. quickstart, webapp und mojo). Dabei wird auch eine vorläufige pom.xml
angelegt.
Artefakt
Mit Artefakt sind im Zusammenhang mit Maven Arbeitsergebnisse gemeint. MavenProjekte haben in der Regel ein Hauptergebnis-Artefakt, oft eine jar-, war- oder earDatei. Aber auch andere Ergebnisse wie Projektdokumentationen können als Artefakt
bezeichnet werden.
POM und pom.xml
POM bedeutet "Project Object Model" und wird repräsentiert durch eine XML-Datei,
üblicherweise die pom.xml, die als zentrale Projektbeschreibungs- und -steuerungsdatei
Meta-Daten zum Projekt enthält, unterteilt in die fünf Bereiche Koordinaten,
Projektbeziehungen, Projektinformationen, Projekteinstellungen und Projektumgebung.
Wichtige Einträge sind zum Beispiel: groupId, artifactId, version, packaging,
build, dependencies, profiles und properties.
Die möglichen Elemente sind hier aufgeführt.
Weitere Informationen finden Sie hier und hier.
Koordinaten
Mit "Koordinaten" werden die fünf ein Artefakt identifizierenden
Informationsbestandteile bezeichnet. Oft sind vor allem die drei wichtigsten gemeint:
groupId, artifactId und version. Zu den Koordinaten gehören aber auch der
optionale classifier und der durch das packaging definierte type, da auch hierüber
verschiedene Artefakte differenziert werden.
groupId
Die groupId ist eine Gruppierungsbezeichnung (ähnlich den Java-Package-Namen).
Normalerweise wird der umgekehrte Domainname der Firma verwendet, eventuell
ergänzt um den Abteilungs- und/oder Projektnamen, zum Beispiel
"de.meinefirma.meinprojekt". Im Maven-Repository repräsentieren die durch Punkte
(".") getrennte Bestandteile Unterverzeichnisse, ähnlich wie bei Package-Namen in
Sourcecodeverzeichnissen.
artifactId
Die artifactId ist der Name des Hauptergebnis-Artefakts dieses Projekts. Der
vollständige Dateiname des Hauptergebnis-Artefakts wird meistens gebildet aus:
"<artifactId>-<version>.<type>", also zum Beispiel "junit-3.8.1.jar" oder
"MeineApp-1.0-SNAPSHOT.jar". Falls ein classifier definiert ist, wird auch dieser
noch dem Dateinamen hinzugefügt (hinter der Versionsbezeichnung), zum Beispiel so:
"testng-5.11-jdk15.jar" und "MeineEjb-1.0-client.jar".
version
Die version des Projekts, typischerweise im Format: "<Major>.<Minor>.<Bugfix><Qualifier>-<Buildnumber>" (wobei die vorderen Teile bei Versionsvergleichen
numerisch gewertet werden (1.10 ist aktueller als 1.9) und die hinteren Teile optional
sind). Lautet der Qualifier "SNAPSHOT", bedeutet dies, dass sich das Projekt noch in
Entwicklung befindet und die Versionsnummer nicht bei jedem Build hochgezählt wird
(was bei Versionsvergleichen eine Rolle spielt).
Es können auch Versionsbereiche angegeben werden, beispielsweise:
4.7 bedeutet: Version 4.7,
[3.8.1,) bedeutet: alle Versionen ab mindestens 3.8.1,
[3.8.1,4.7) bedeutet: alle Versionen ab 3.8.1 bis ausschließlich 4.7,
[3.8.1,4.7] bedeutet: alle Versionen ab 3.8.1 bis einschließlich 4.7
classifier
Einige Ergebnis-Artefakte können in unterschiedlichen Ausführungen vorliegen, die im
Dateinamen durch den classifier unterschieden werden. Zum Beispiel das Assembly
Plugin erstellt ein zusätzliches durch einen classifier unterschiedenes ErgebnisArtefakt (z.B. MeineApp-1.0-jar-with-dependencies.jar). TestNG gibt es für
verschiedene Java-Versionen (z.B. testng-5.11-jdk15.jar). Oder beim Build eines
EJB-Moduls wird eine MeineEjb-1.0.jar für das Deployment zum App-Server und per
generateClient eine MeineEjb-1.0-client.jar für die Client-Applikation erstellt.
packaging
Das packaging definiert den Typ des Hauptergebnis-Artefakts, zum Beispiel jar, war,
ear, pom oder maven-plugin.
profile
Ein profile definiert Voreinstellungen. Über Umgebungsbedingungen,
Kommandozeilenoptionen oder andere Parameter können Profile aktiviert werden.
Siehe auch die Beispiele Erweiterung um Maven-Profile und Integrationstest mit Cargo
sowie Introduction to Build Profiles.
property
Ein property definiert ein Name/Wert-Paar. Siehe auch das Beispiel Maven-PropertiesProjekt.
dependency
Abhängigkeiten werden als dependency eingetragen (transitive Abhängigkeiten brauchen
nicht eingetragen zu werden). Siehe auch Dependency Mechanism und das Beispiel zum
Dependency-Management.
Die eingetragenen Artefakte werden beim Build im Repository benötigt.
Über das scope-Attribut wird gesteuert, für welche Buildprozessphase die Abhängigkeit
benötigt wird.
scope
Bei dependency-Einträgen kann über das scope-Attribut eine Art Sichtbarkeit definiert
werden, nämlich in welchen Phasen des Buildprozesses die Abhängigkeit benötigt wird.
Nur in den spezifizierten Phasen werden die eingetragenen Module dem CLASSPATH
hinzugefügt. Die wichtigsten Ausprägungen sind: compile (Compilierphase), provided
(ähnlich wie compile, aber Artefakt wird zur Laufzeit von Laufzeitumgebung (z.B. JEEContainer) bereitgestellt), runtime (Laufzeit) und test (Tests).
Repository
Es wird unterschieden zwischen dem lokalen Maven-Repository und RemoteRepositories.
Das lokale Maven-Repository dient hauptsächlich als Cache-Zwischenspeicher für
Remote-Repositories und als Austauschverzeichnis für lokal installierte Artefakte. Von
Remote-Repositories werden benötigte und noch nicht im lokalen Repository vorhandene
Artefakte geladen.
Zusätzlich zum Standard-Repository (meistens http://central.maven.org/maven2) können
auch zusätzliche Repositories definiert werden.
Siehe auch Introduction to Repositories und die Liste einiger häufig verwendeter
Artefakte.
Site
Mit Site ist eine Art Website gemeint, also eine Zusammenstellung von Webseiten, die
als Projektdokumentation dient.
Siehe auch das Beispiel Site Report um Project Reports zur Sourcecodeanalyse erweitern.
Mojo
In Java programmierte Maven-Plugins bestehen aus Mojos. Ein Mojo ("Maven (plain)
old Java Object") ist eine Java-Klasse die das Interface
org.apache.maven.plugin.Mojo implementiert (oder
org.apache.maven.plugin.AbstractMojo erweitert) und damit ein Plugin-Goal
realisiert.
Siehe auch das Beispiel Eigenes Maven-Plugin sowie guide-java-plugin-development,
maven-plugin-api und mojo-api-specification.
Eindeutigkeit
Maven stellt einen großen Fortschritt für die Eindeutigkeit und Reproduzierbarkeit von
Build-Ergebnissen dar. Aber auch Maven hat Lücken: Wenn in der pom.xml für Plugins
keine Versionsnummer angegeben wird, können bei verschiedenen Builds (anderer
Zeitpunkt oder anderer Rechner) unterschiedliche Ergebisse entstehen, je nachdem,
welche Plugin-Version sich entweder zufällig im jeweiligen lokalen Repository befindet
oder in der jeweiligen Maven-Super-POM vordefiniert ist (bedenken Sie, dass in der
Maven-Super-POM nur die wichtigsten Plugins vordefiniert sind).
mvn-Kommandos
Die Aufruf-Syntax der im Kommandozeilenfenster aufrufbaren mvn-Kommandos lautet:
"mvn [options] [<goal(s)>] [<phase(s)>]".
Es können also übergeben werden (sowohl einzeln als auch kombiniert):

Mit einem Minuszeichen ("-") beginnende Optionen (z.B. -D, -P, -B, -X, -e, siehe
"mvn -h")


Einzelne Goals (bestehend aus dem Plugin-Namen und dem Goal-Namen,
getrennt durch einen Doppelpunkt (":"), z.B. archetype:generate und
jetty:run)
Lifecycle-Phasen (z.B. clean, compile, test, package, integration-test,
verify, install, deploy und site)
Einige wichtige oder häufig benutzte Maven-Kommandos sind:
Kommando
Bedeutung
mvn -v
Anzeige der Version von Maven
mvn -h
Hilfe zu den Kommandozeilenoptionen von Maven
mvn help:help
Hilfe zur aktuellen Build-Umgebung
mvn help:describe -Dplugin=...
Hilfe zu bestimmten Plugins (und mit -Ddetail auch zu
Goals)
mvn help:effective-settings
Anzeige der aktuellen projektübergreifenden SettingsEinstellungen
mvn help:effective-pom
Anzeige der aktuell resultierenden projektbezogenen POM
mvn help:active-profiles
Anzeige der aktiven Profile (aber ohne geerbte Profile)
mvn help:all-profiles
Anzeige aller Profile (aber ohne geerbte Profile)
mvn help:evaluate -Dexpression=...
Auflösung von Maven-Ausdrücken, z.B. in Batchdatei:
for /f "delims=" %%a in ('mvn
help:evaluate Dexpression^=settings.localRepository ^|
findstr /V [') do set _mvnrepo=%%a
Auch Server-Props können abgefragt werden, z.B.:
mvn help:evaluate Dexpression=settings.servers[0].username
mvn dependency:tree -Dverbose
Übersicht zum Abhängigkeitsbaum; weitere Goals:
analyze, build-classpath, resolveplugins, go-offline, copy-dependencies
mvn dependency:sources
Zusätzlich zu den Libs und POMs auch die Sourcen
downloaden, falls vorhanden
mvn dependency:resolve -Dclassifier=javadoc
Zusätzlich zu den Libs und POMs auch die JavadocDateien downloaden
mvn clean
Löschen aller erzeugten Artefakte und des targetVerzeichnisses
mvn compile
Kompilierung
mvn test
Ausführen der Komponententests (z.B. JUnit-Tests)
mvn package
Build und Erzeugung der Ergebnis-Artefakte
mvn verify
Ausführen der Integrationstests (nicht "mvn integrationtest", damit auch die "post-integration-test"-Phase
ausgeführt wird)
mvn install
Kopieren des Ergebnis-Artefakts ins lokale MavenRepository
mvn deploy
Kopieren des Ergebnis-Artefakts ins Remote Repository
mvn site
Erzeugen der Projektdokumentation
mvn archetype:generate
Projektstruktur erzeugen
mvn test -Dtest=MeinTest
Nur eine einzige Testklasse ausführen
mvn test -Dtest=MeinAbcTest,MeinXyzTest
Mehrere Testklassen in definierter Reihenfolge ausführen
mvn package -DskipTests
Test-Klassen werden kompiliert, aber nicht ausgeführt
mvn package -Dmaven.test.skip=true
Test-Klassen werden nicht kompiliert und nicht ausgeführt
mvn jetty:run
Startet den Jetty-Server und führt das Webapp-Projekt aus
Installation von Maven
Maven-Basisinstallation unter Linux

Siehe: Installation von Maven unter Ubuntu-Linux.
Maven-Basisinstallation unter Windows
1. Installieren Sie ein aktuelles Java SE JDK.
2. Downloaden Sie Maven 3.5 von http://maven.apache.org (z.B. apache-maven-3.5.0bin.zip).
3. Entzippen Sie die Maven-.zip-Datei in ein beliebiges Verzeichnis, zum Beispiel nach
D:\Tools, und umbenennen Sie das resultierende Verzeichnis, zum Beispiel nach
D:\Tools\Maven3.
4. Setzen Sie folgende Umgebungsvariablen (Environment-Variablen) (passen Sie die
Pfadangaben an Ihre Java- und Maven-Verzeichnisse an) (unter Windows 7 und Vista: 'WindowsTaste +
PauseTaste' | 'Erweiterte Systemeinstellungen' | Reiter 'Erweitert' | 'Umgebungsvariablen...'; unter Windows
XP: 'WindowsTaste + PauseTaste' | 'Erweitert' | 'Umgebungsvariablen'; unter Linux: siehe
linux.htm#Umgebungsvariablen):
Benutzervariablen setzen:
JAVA_HOME
C:\Program Files\Java\jdk1.8
M2_HOME
D:\Tools\Maven3
MAVEN_OPTS -Xms256m -Xmx512m
PATH
%JAVA_HOME%\bin;%M2_HOME%\bin
5. Bitte beachten Sie, dass Sie nicht die PATH-Systemvariable um die "%...%\bin"-Pfade
erweitern, sondern eine PATH-Benutzervariable anlegen (sonst funktioniert die "%...%"Syntax nicht).
6. Jetzt müssen folgende Kommandos ein erfolgreiches Ergebnis liefern. Öffnen Sie (in
Windows mit 'Windows-Taste' + 'R' und 'cmd') ein neues Kommandozeilenfenster (damit
die Änderung der Umgebungsvariablen wirksam wird) und geben Sie ein:
set
set JAVA_HOME
set M2_HOME
path
java -version
javac -version
javac -help
mvn -v
mvn -h
7. Weitere Installationshinweise finden Sie hier.
Konfiguration
1. Fehlende Bibliotheken und Plug-ins lädt Maven automatisch zum Beispiel von
http://central.maven.org/maven2. Beachten Sie auch die Hinweise zu Sun JARs.
2. Wenn Sie die Voreinstellung nicht ändern, wird als lokales Maven-RepositoryVerzeichnis je nach Betriebssystem zum Beispiel
C:\Users\%USERNAME%\.m2\repository,
C:\Dokumente und Einstellungen\%USERNAME%\.m2\repository, <userhome>/.m2/repository oder ~/.m2/repository verwendet.
3. Für alle Projekte geltende Voreinstellungen (z.B. Zugangsdaten zu CVS- oder
Subversion-Servern) werden in settings.xml-Dateien definiert:
%M2_HOME%\conf\settings.xml für systemweite Einstellungen,
%USERPROFILE%\.m2\settings.xml für benutzerspezifische Einstellungen.
Projektabhängige Einstellungen befinden sich in den jeweiligen pom.xml-Dateien.
4. Falls Sie spezielle Konfigurationen benötigen, wie zum Beispiel HTTP-Proxy-Server,
Repository-Manager oder Server-Authentifizierung, lesen Sie bitte Maven-Repository,
Configuring Maven, Settings Reference, Technical Settings Descriptor und Password
Encryption.
5. Falls Sie eine Fehlermeldung ähnlich zu folgender erhalten:
Failed to invoke Maven build. Error configuring command-line. Reason:
Maven executable not found at: ...\bin\mvn.bat
Dann prüfen Sie, ob sich in Ihrem Maven-bin-Verzeichnis eine mvn.bat-Datei befindet.
Falls nicht, führen Sie aus:
cd /D %M2_HOME%\bin
dir mvn.*
if not exist mvn.bat copy mvn.cmd mvn.bat
Maven in Firmen
1. In Firmen sind in der systemweiten settings.xml meistens zumindest Einstellungen
zum localRepository und eines mirrors erforderlich, beispielsweise so:
2.
3. <settings>
4.
<localRepository>D:\Tools\Maven3-Repo</localRepository>
5.
<mirrors>
6.
<mirror>
7.
<id>MeineMirrorId</id>
8.
<mirrorOf>central</mirrorOf>
9.
<url>http://firma.repository.server:port/repo/path</url>
10.
</mirror>
11.
</mirrors>
12. </settings>
Das localRepository-Verzeichnis sollte in Firmen nicht im <user-home>-Verzeichnis
liegen, weil in Firmen häufig das <user-home>-Verzeichnis bei jedem PCHerunterfahren gesichert und beim Starten wiederhergestellt wird, was unnötig BackupSpeicherplatz kosten und diese Vorgänge verlangsamen würde.
Ein mirror-Server muss oft definiert werden, damit Artefakte nicht vom Internet,
sondern von einem firmeninternen Server geladen werden, weil in Firmen eventuell
restriktivere Sicherheitsvorschriften gelten oder weil vielleicht nur bestimmte Libs und
Versionen zur Verwendung freigegeben sind.
Sehen Sie sich die eingestellten Settings an über:
mvn help:effective-settings
Weiteres zur settings.xml und zum Umgang mit Firmen-Proxies und RepositoryManager finden Sie weiter unten unter Maven-Repository.
Maven mit Eclipse mit M2E (empfohlen)
1. Wenn Sie Eclipse verwenden, sollten Sie normalerweise nicht das maven-eclipseplugin verwenden, sondern stattdessen Maven-Projekte mit dem M2Eclipse-Plugin
importieren. Siehe hierzu: Das M2Eclipse-Plugin (m2e) zur Zusammenarbeit von Eclipse
mit Maven.
2. Um in Eclipse ein Projektmodul aus CVS, Subversion oder Mercurial (hg) zu laden (oder
upzudaten), verfahren Sie wie beschrieben unter CVS, Subversion bzw. Mercurial (hg).
3. Falls Sie Maven-Multimodulprojekte in Eclipse bearbeiten wollen, sehen Sie sich an:
maven-multiproj.htm#Maven-Multimodulprojekt-in-Eclipse und Using maven-eclipseplugin in multi-module projects with WTP.
Maven mit Eclipse ohne M2E (nicht empfohlen)
1. Wenn Sie unbedingt das maven-eclipse-plugin verwenden wollen, obwohl es nicht
empfohlen ist:
Geben Sie das lokale Maven-Repository-Verzeichnis über die Eclipse-Variable
"M2_REPO" bekannt. Dies geht am einfachsten über das Maven-Goal
eclipse:configure-workspace über folgenden Kommandozeilenbefehl (bitte EclipseWorkspace-Pfad "D:\MeinWorkspace" anpassen):
mvn -Declipse.workspace=D:\MeinWorkspace eclipse:configure-workspace
Alternativ können Sie in Eclipse über 'Window' | 'Preferences' | '[+] Java' | '[+] Build Path'
| 'Classpath Variables' | 'New...' eingeben (bitte Pfad anpassen):
Eclipse Classpath Variables:
M2_REPO C:\Users\%USERNAME%\.m2\repository
Bzw. je nach Konfiguration:
M2_REPO D:\Tools\Maven3-Repo
Anschließend müssen Sie Eclipse neu starten.
2. Bei neuen Maven-Projekten oder nach Updates auf bestehenden Maven-Projekten
müssen Sie als Erstes die Eclipse-Projektdateien erstellen bzw. aktualisieren. Falls Sie
nicht das M2Eclipse-Plugin verwenden, erfolgt das über "mvn eclipse:..."Kommandos:
mvn eclipse:eclipse
Falls Sie nachträglich Konfigurationsänderungen durchführen, kann folgendes
Kommando "aufräumen":
mvn eclipse:clean eclipse:eclipse
3. Falls das Projekt noch nicht in Eclipse existiert: Importieren Sie es über: 'File' | 'Import...'
| 'Existing Projects into Workspace' | 'Select root directory'.
4. Wichtig: Nach dem "mvn eclipse:eclipse"-Kommando und auch nach jeder anderen
Änderung, die Sie außerhalb von Eclipse vornehmen, müssen Sie in Eclipse im 'Package
Explorer' die Projektmodule markieren und mit 'F5' einen 'Refresh' ausführen.
Maven mit Java 9 und Jigsaw
1. Falls Sie mit Java 9 und Jigsaw arbeiten wollen, beachten Sie folgende Hinweise:
Für Jigsaw-Module wird manchmal eine vom üblichen Maven-Standard abweichende
Verzeichnisstruktur empfohlen, siehe hierzu: Verzeichnisstruktur für Java-9-JigsawModule.
Falls Sie mit Maven JUnit-Tests ausführen wollen: Im Juli 2017 war dies mit dem Maven
Compiler Plugin in der Version 3.6.1 auf Grund eines Fehlers nicht möglich, siehe
hierzu: Jigsaw-Dep-Demo mit Maven und Maven Compiler Plugin: MCOMPILER-294:
test-compile broken due to -Xmodule removal in jdk-ea167.
Maven-Hello-World-Projekt
1. Öffnen Sie ein Kommandozeilenfenster ('Windows-Taste' + 'R', 'cmd'), wechseln Sie in
Ihr Projekte-Verzeichnis (z.B. \MeinWorkspace), erzeugen Sie mit dem Mavenarchetype-Plugin eine einfache Standard-Directory-Struktur und sehen Sie sich diese an
(das mvn-Kommando in einer Zeile):
cd \MeinWorkspace
mvn archetype:generate -DinteractiveMode=false DgroupId=de.meinefirma.meinprojekt -DartifactId=MvnHelloApp
cd MvnHelloApp
tree /F
Sie erhalten:
[\MeinWorkspace\MvnHelloApp]
|- [src]
|
|- [main]
|
|
'- [java]
|
|
'- [de]
|
|
'- [meinefirma]
|
|
'- [meinprojekt]
|
|
'- App.java
|
'- [test]
|
'- [java]
|
'- [de]
|
'- [meinefirma]
|
'- [meinprojekt]
|
'- AppTest.java
'- pom.xml
2. Um den Build durchzuführen und das entstandene Programm auszuführen, geben Sie im
Kommandozeilenfenster ein:
cd \MeinWorkspace\MvnHelloApp
mvn package
java -cp target/MvnHelloApp-1.0-SNAPSHOT.jar
de.meinefirma.meinprojekt.App
Sie erhalten:
Hello World!
3. Sehen Sie nach, was im target-Verzeichnis entstanden ist und was in der resultierenden
Jar-Datei enthalten ist:
cd \MeinWorkspace\MvnHelloApp
tree /F
jar tvf target/MvnHelloApp-1.0-SNAPSHOT.jar
4. Fügen Sie in App.java etwas Sinnvolles ein und testen Sie dies in AppTest.java.
5. Folgeaufrufe von "mvn package" gehen wesentlich schneller, weil jetzt die benötigten
Maven-Plugins im lokalen Maven-Repository vorhanden sind.
Ausführbare Jar-Datei
Im letzten Beispiel wurde direkt die kompilierte Klasse App.class ausgeführt. Im folgenden
Beispiel wird das letzte Beispiel so erweitert, dass die kompilierten Klassen mit dem Maven JAR
Plugin zu einem ausführbaren Jar-Archiv zusammengefasst werden, inklusive einer automatisch
generierten MANIFEST.MF mit geignetem Main-Class-Eintrag.
Falls Sie zusätzlich Libs einbinden wollen, sehen Sie sich die Beispiele zum maven-assemblyplugin und zum maven-shade-plugin an.
1. Ersetzen Sie im MvnHelloApp-Projektverzeichnis den Inhalt der pom.xml durch:
2.
3. <project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4.
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
5.
<modelVersion>4.0.0</modelVersion>
6.
<groupId>de.meinefirma.meinprojekt</groupId>
7.
<artifactId>MvnHelloApp</artifactId>
8.
<version>1.0-SNAPSHOT</version>
9.
<packaging>jar</packaging>
10.
<name>MvnHelloApp</name>
11.
<build>
12.
<plugins>
13.
<plugin>
14.
<groupId>org.apache.maven.plugins</groupId>
15.
<artifactId>maven-jar-plugin</artifactId>
16.
<version>3.0.2</version>
17.
<configuration>
18.
<archive>
19.
<manifest>
20.
<mainClass>de.meinefirma.meinprojekt.App</mainClass>
21.
<addClasspath>true</addClasspath>
22.
</manifest>
23.
</archive>
24.
</configuration>
25.
</plugin>
26.
</plugins>
27.
</build>
28.
<dependencies>
29.
<dependency>
30.
<groupId>junit</groupId>
31.
<artifactId>junit</artifactId>
32.
<version>4.12</version>
33.
<scope>test</scope>
34.
</dependency>
35.
</dependencies>
36. </project>
37. Führen Sie die Jar-Datei im Kommandozeilenfenster aus:
cd \MeinWorkspace\MvnHelloApp
mvn package
java -jar target/MvnHelloApp-1.0-SNAPSHOT.jar
Sie erhalten wieder:
Hello World!
38. Sie können auch weiterhin die App.class direkt ausführen (wie im obigen Beispiel):
java -cp target/MvnHelloApp-1.0-SNAPSHOT.jar
de.meinefirma.meinprojekt.App
39. Sehen Sie sich die generierte MANIFEST.MF mit dem Main-Class-Eintrag an:
cd \MeinWorkspace\MvnHelloApp
jar xvf target/MvnHelloApp-1.0-SNAPSHOT.jar META-INF/MANIFEST.MF
type META-INF\MANIFEST.MF
Sie enthält die Zeile:
Main-Class: de.meinefirma.meinprojekt.App
40. Übrigens kann das maven-jar-plugin noch mehr, beispielsweise können Sie mit dem
test-jar-Goal Testartefakte auch in anderen Modulen nutzen, siehe Test-Jar. Sie
können auch mehrere verschiedene Artifakte mit einem Aufruf erzeugen, siehe RESTClient mit REST-JUnit-Test.
41. Falls Sie die Attribute in der MANIFEST.MF zur Laufzeit auslesen wollen, ersetzen Sie den
Inhalt der App.java durch:
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
package de.meinefirma.meinprojekt;
import java.io.File;
import java.util.jar.*;
public class App
{
public static void main( String[] args ) throws Exception
{
System.out.println( "\nManifest-Attribute:\n" +
getJarManifestAttributes( App.class ).entrySet() );
53.
}
54.
55.
public static Attributes getJarManifestAttributes( Class<?> clazz )
throws Exception
56.
{
57.
JarFile jarFile = new JarFile( new File(
clazz.getProtectionDomain().getCodeSource().getLocation().toURI() ) );
58.
Manifest manifest = jarFile.getManifest();
59.
jarFile.close();
60.
return manifest.getMainAttributes();
61.
}
62. }
Und führen Sie aus:
mvn package
java -jar target/MvnHelloApp-1.0-SNAPSHOT.jar
Ausführbare Jar-Datei inklusive Abhängigkeiten mit dem Assembly Plugin
Im folgenden Beispiel besteht die Anwendung aus einer Klasse, die eine zusätzliche .jarBibliothek benötigt. Die kompilierten Klassen, die benötigten .jar-Libs und eine automatisch
generierte MANIFEST.MF werden mit dem Maven Assembly Plugin in ein ausführbares "Fat-Jar"
gepackt.
Alternativ zum maven-assembly-plugin wird mittlerweile häufig das maven-shade-plugin
bevorzugt. Siehe hierzu das Beispiel Vert.x-HelloWorld.
1. Starten Sie ein neues Projekt:
cd \MeinWorkspace
mvn archetype:generate -DinteractiveMode=false DgroupId=de.meinefirma.meinprojekt -DartifactId=MvnJarMitLibs
cd MvnJarMitLibs
2. Ersetzen Sie im src\main\java\de\meinefirma\meinprojekt-Verzeichnis den Inhalt
von App.java durch:
3.
4. package de.meinefirma.meinprojekt;
5.
6. import org.apache.log4j.Logger;
7.
8. public class App
9. {
10.
private static final Logger LOGGER = Logger.getRootLogger();
11.
12.
public static void main( String[] args )
13.
{
14.
LOGGER.info( "---- Hallo Logger! ----" );
15.
}
16. }
Die Anwendung verwendet den Log4j-Logger. Falls Sie Log4j nicht kennen: Sehen Sie
sich java-log4j.htm an.
17. Ersetzen Sie im src\test\java\de\meinefirma\meinprojekt-Verzeichnis den Inhalt
von AppTest.java durch:
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
package de.meinefirma.meinprojekt;
import junit.framework.TestCase;
public class AppTest extends TestCase
{
public void testApp()
{
App.main( null );
}
}
Erzeugen Sie im src\main-Verzeichnis das Unterverzeichnis resources
folgende Log4j-Konfigurationsdatei: log4j.properties
und darin
31.
32. log4j.rootLogger=DEBUG, MeinConsoleAppender
33. log4j.appender.MeinConsoleAppender=org.apache.log4j.ConsoleAppender
34. log4j.appender.MeinConsoleAppender.layout=org.apache.log4j.PatternLayo
ut
35. log4j.appender.MeinConsoleAppender.layout.ConversionPattern=%d{ISO8601
} %-5p [%t] %c: %m%n
36. Die Abhängigkeit zur log4j-1.2.17.jar muss in die pom.xml eingetragen werden.
Ersetzen Sie im MvnJarMitLibs-Projektverzeichnis den Inhalt der pom.xml durch:
37.
38. <project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
39.
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
40.
<modelVersion>4.0.0</modelVersion>
41.
<groupId>de.meinefirma.meinprojekt</groupId>
42.
<artifactId>MvnJarMitLibs</artifactId>
43.
<version>1.0-SNAPSHOT</version>
44.
<packaging>jar</packaging>
45.
<name>MvnJarMitLibs</name>
46.
<dependencies>
47.
<dependency>
48.
<groupId>log4j</groupId>
49.
<artifactId>log4j</artifactId>
50.
<version>1.2.17</version>
51.
</dependency>
52.
<dependency>
53.
<groupId>junit</groupId>
54.
<artifactId>junit</artifactId>
55.
<version>4.12</version>
56.
<scope>test</scope>
57.
</dependency>
58.
</dependencies>
59. </project>
60. Sehen Sie sich mit tree /F die Verzeichnisstruktur an:
61.
62. [\MeinWorkspace\MvnJarMitLibs]
63.
|- [src]
64.
|
|- [main]
65.
|
|
|- [java]
66.
|
|
|
'- [de]
67.
|
|
|
'- [meinefirma]
68.
|
|
|
'- [meinprojekt]
69.
|
|
|
'- App.java
70.
|
|
'- [resources]
71.
|
|
'- log4j.properties
72.
|
'- [test]
73.
|
'- [java]
74.
|
'- [de]
75.
|
'- [meinefirma]
76.
|
'- [meinprojekt]
77.
|
'- AppTest.java
78.
'- pom.xml
79. Führen Sie den JUnit-Test aus:
mvn test
Sie erhalten:
2015-01-01 11:12:13,456 INFO
[main] root: ---- Hallo Logger! ----
Im Test ist die log4j-1.2.17.jar im Classpath und der Log4j-Logger funktioniert.
80. Führen Sie den Build-Prozess aus, sehen Sie sich an, was in der erstellten Jar-Datei
enthalten ist und versuchen Sie die Anwendung zu starten:
mvn package
jar tvf target/MvnJarMitLibs-1.0-SNAPSHOT.jar
java -cp target/MvnJarMitLibs-1.0-SNAPSHOT.jar
de.meinefirma.meinprojekt.App
Weil in der erstellten Jar-Datei die log4j-1.2.17.jar-Lib fehlt, erhalten Sie die
Exception:
Exception in thread "main" java.lang.NoClassDefFoundError:
org/apache/log4j/Logger
81. Um eine ausführbare Jar-Datei zu erhalten, sind zwei Erweiterungen notwendig:
 Libs und andere Abhängigkeiten müssen in die Jar-Datei integriert werden.
 Eine META-INF/MANIFEST.MF muss hinzugefügt werden inklusive eines MainClass-Eintrags.
Dies leistet das maven-assembly-plugin.
Ersetzen Sie im MvnJarMitLibs-Projektverzeichnis den Inhalt der pom.xml durch:
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>de.meinefirma.meinprojekt</groupId>
<artifactId>MvnJarMitLibs</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>MvnJarMitLibs</name>
<build>
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.0.0</version>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<archive>
<manifest>
<mainClass>de.meinefirma.meinprojekt.App</mainClass>
</manifest>
</archive>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
<descriptorRef>jar-with-dependencies</descriptorRef>
ist ein vordefinierter
"prefabricated assembly descriptor" zur Integration von Abhängigkeiten, wie zum
Beispiel .jar-Libs. Weitere Pre-defined Descriptor Files sind: bin, src und project.
Alternativ können Sie auch einen selbst erstellten "custom assembly descriptor"
verwenden, wie unter Configuration and Usage beschrieben ist.
<mainClass>de.meinefirma.meinprojekt.App</mainClass> definiert die
auszuführende Klasse, welche die main()-Methode enthält.
82. Bauen Sie die "jar-with-dependencies" und führen Sie sie aus:
mvn clean package
dir target\MvnJarMitLibs*.jar
jar tvf target\MvnJarMitLibs-1.0-SNAPSHOT-jar-with-dependencies.jar
--> Die org/apache/log4j/...-Klassen sind enthalten.
java -jar target\MvnJarMitLibs-1.0-SNAPSHOT-jar-with-dependencies.jar
--> Die Jar-Datei kann ausgeführt werden und Sie erhalten das korrekte Ergebnis:
2015-01-01 11:12:13,456 INFO
[main] root: ---- Hallo Logger! ----
83. Sehen Sie sich die Hilfstexte zu den Goals des Assembly-Plugins an:
mvn help:describe -Dplugin=org.apache.maven.plugins:maven-assemblyplugin
Maven-Webapp-Projekt mit Jetty
1. Öffnen Sie ein Kommandozeilenfenster ('Windows-Taste' + 'R', 'cmd'), wechseln Sie in
Ihr Projekte-Verzeichnis (z.B. \MeinWorkspace) und erzeugen Sie eine WebappProjektstruktur (das mvn-Kommando in einer Zeile):
cd \MeinWorkspace
mvn archetype:generate -DinteractiveMode=false DarchetypeArtifactId=maven-archetype-webapp DgroupId=de.meinefirma.meinprojekt -DartifactId=MvnJettyWebApp
cd MvnJettyWebApp
tree /F
Sie erhalten:
[\MeinWorkspace\MvnJettyWebApp]
|- [src]
|
'- [main]
|
|- [resources]
|
'- [webapp]
|
|- [WEB-INF]
|
|
'- web.xml
|
'- index.jsp
'- pom.xml
2. Ersetzen Sie den Inhalt der pom.xml durch:
3.
4. <project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
5.
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
6.
<modelVersion>4.0.0</modelVersion>
7.
<groupId>de.meinefirma.meinprojekt</groupId>
8.
<artifactId>MvnJettyWebApp</artifactId>
9.
<version>1.0-SNAPSHOT</version>
10.
<packaging>war</packaging>
11.
<name>MvnJettyWebApp Maven Webapp</name>
12.
<url>http://www.meinefirma.de</url>
13.
<build>
14.
<finalName>${project.artifactId}</finalName>
15.
<plugins>
16.
<plugin>
17.
<groupId>org.eclipse.jetty</groupId>
18.
<artifactId>jetty-maven-plugin</artifactId>
19.
<version>9.3.10.v20160621</version>
20.
<configuration>
21.
<webAppConfig>
22.
<contextPath>/${project.artifactId}</contextPath>
23.
</webAppConfig>
24.
</configuration>
25.
</plugin>
26.
</plugins>
27.
</build>
28. </project>
29. Ausführung des Projekts:
cd \MeinWorkspace\MvnJettyWebApp
mvn jetty:run
Warten Sie, bis "Started Jetty Server" erscheint, und rufen Sie dann im Webbrowser
auf:
http://localhost:8080/MvnJettyWebApp
Sie erhalten eine Webseite mit:
Hello World!
30. Beenden Sie Jetty mit "Strg + C".
31. Sehen Sie sich die Hilfstexte zu dem genannten und anderen Goals des Jetty-Plugins an:
mvn help:describe -Dplugin=org.eclipse.jetty:jetty-maven-plugin
Sehen Sie sich Konfigurationsoptionen des Jetty-Plugins an:
http://eclipse.org/jetty/documentation/current/jetty-maven-plugin.html.
Maven-Webapp mit Properties im Manifest
Dieses Beispiel demonstriert Folgendes:



Das automatische Hinzufügen einer MANIFEST.MF-Datei zu einer Webanwendung (WARDatei).
Das Hinzufügen zusätzlicher Attribute zu dieser MANIFEST.MF-Datei.
Das Auslesen der Attribute der MANIFEST.MF-Datei innerhalb der Webanwendung (z.B.
in einer JSP).
Führen Sie folgende Schritte durch:
1. Öffnen Sie ein Kommandozeilenfenster, wechseln Sie in Ihr Projekte-Verzeichnis (z.B.
\MeinWorkspace) und erzeugen Sie eine Webapp-Projektstruktur:
cd \MeinWorkspace
mvn archetype:generate -DinteractiveMode=false DarchetypeArtifactId=maven-archetype-webapp DgroupId=de.meinefirma.meinprojekt -DartifactId=MvnWebManifest
cd MvnWebManifest
tree /F
Sie erhalten:
[\MeinWorkspace\MvnWebManifest]
|- [src]
|
'- [main]
|
|- [resources]
|
'- [webapp]
|
|- [WEB-INF]
|
|
'- web.xml
|
'- index.jsp
'- pom.xml
2. Ersetzen Sie den Inhalt der pom.xml durch:
3.
4. <project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
5.
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
6.
<modelVersion>4.0.0</modelVersion>
7.
<groupId>de.meinefirma.meinprojekt</groupId>
8.
<artifactId>MvnWebManifest</artifactId>
9.
<version>1.0-SNAPSHOT</version>
10.
<packaging>war</packaging>
11.
<name>MvnWebManifest</name>
12.
<url>http://www.meinefirma.de</url>
13.
<properties>
14.
<maven.build.timestamp.format>yyyy-MM-dd
HH:mm</maven.build.timestamp.format>
15.
</properties>
16.
<build>
17.
<finalName>${project.artifactId}</finalName>
18.
<plugins>
19.
<plugin>
20.
<artifactId>maven-war-plugin</artifactId>
21.
<version>2.6</version>
22.
<configuration>
23.
<!-- <packagingExcludes>, um folgende Warnung mit der 2.1.1Version zu vermeiden:
24.
[WARNING] Warning: selected war files include a WEBINF/web.xml which will be ignored
25.
(webxml attribute is missing from war task, or
ignoreWebxml attribute is specified as 'true') -->
26.
<packagingExcludes>WEB-INF/web.xml</packagingExcludes>
27.
<archive>
28.
29.
30.
<manifest>
<!-- Default-Properties: -->
<addDefaultImplementationEntries>false</addDefaultImplementationEntries
>
31.
</manifest>
32.
<manifestEntries>
33.
<!-- Maven-Properties: -->
34.
<Modulname>${project.name}</Modulname>
35.
<Modulversion>${project.version}</Modulversion>
36.
<Maven-Build-Timestamp>${maven.build.timestamp}</MavenBuild-Timestamp>
37.
<!-- Andere Properties, z.B. per buildnumber-mavenplugin, von Jenkins/Hudson oder per Kommandozeile gesetzt: -->
38.
<VCS-Buildnumber>${buildNumber}</VCS-Buildnumber>
39.
<hg-ChangeSet-ID>${changeSet}, ${changeSetDate}</hgChangeSet-ID>
40.
<Modulbuild>${BUILD_TAG}, ${BUILD_ID}</Modulbuild>
41.
<MeinSpezProp>${MeinSpezProp}</MeinSpezProp>
42.
</manifestEntries>
43.
</archive>
44.
</configuration>
45.
</plugin>
46.
<plugin>
47.
<groupId>org.eclipse.jetty</groupId>
48.
<artifactId>jetty-maven-plugin</artifactId>
49.
<version>9.4.5.v20170502</version>
50.
<configuration>
51.
<webAppConfig>
52.
<contextPath>/${project.artifactId}</contextPath>
53.
</webAppConfig>
54.
</configuration>
55.
</plugin>
56.
</plugins>
57.
</build>
58. </project>
59. Ersetzen Sie im src\main\webapp-Verzeichnis den Inhalt der index.jsp durch:
60.
61. <%@ page import="java.io.*" %>
62. <%@ page import="java.util.*" %>
63. <%@ page import="java.util.jar.*" %>
64.
65. <html>
66. <body>
67. <h2>Hello World!</h2>
68. <%
69.
String pth = getServletConfig().getServletContext().getRealPath(
"/" );
70.
if( !pth.endsWith( "/" ) && !pth.endsWith( "\\" ) ) pth += "/";
71.
File mf = new File( pth + "META-INF/MANIFEST.MF" );
72.
if( mf.exists() ) {
73.
out.println( "<h4>Attribute in der META-INF/MANIFEST.MF:</h4>"
);
74.
Manifest manifest = new Manifest( new FileInputStream( mf ) );
75.
for( Map.Entry<Object,Object> attr :
manifest.getMainAttributes().entrySet() ) {
76.
77.
}
78.
}
79. %>
80. </body>
81. </html>
out.println( attr + "<br>" );
82. Ausführung des Projekts:
cd \MeinWorkspace\MvnWebManifest
mvn clean war:manifest war:war -DMeinSpezProp=AbcXyz
type src\main\webapp\META-INF\MANIFEST.MF
mvn jetty:run
Warten Sie, bis "Started Jetty Server" erscheint, und rufen Sie dann im Webbrowser
auf:
http://localhost:8080/MvnWebManifest
83. Sie erhalten eine Webseite mit der Auflistung der Attribute in der MANIFEST.MF inklusive
des per Kommandozeile angegebenen Attributs MeinSpezProp=AbcXyz.
Das ${buildNumber}-Property kann durch das buildnumber-maven-plugin mit dem
create-Goal gesetzt werden (falls Ihr Versionskontrollsystem unterstützt wird und
korrekt im SCM-Tag eingetragen ist).
Falls Sie Mercurial (hg) als Versionskontrollsystem verwenden, können mit dem
buildnumber-maven-plugin-hgchangeset-Goal die ${changeSet}- und
${changeSetDate}"-Properties gesetzt werden.
Die BUILD_...-Properties können z.B. durch Jenkins/Hudson gesetzt werden.
Falls einige Properties beim oben gezeigten Aufruf nicht gesetzt werden (z.B. weil sie
erst in der validate-Phase gesetzt werden): Führen Sie folgende Kommandos aus, um
sie anzuzeigen:
cd \MeinWorkspace\MvnWebManifest
call mvn clean install
cd target
jar xf MvnWebManifest.war
type META-INF\MANIFEST.MF
84. Beenden Sie Jetty mit "Strg + C".
85. Sie können die resultierende WAR-Datei target\MvnWebManifest.war auch in
beliebige andere Web Application Server (z.B. Tomcat) deployen.
Maven-Properties-Projekt mit Ressourcen-Filterung
Dieses Beispiel zeigt Zweierlei:


Es gibt verschiedene Möglichkeiten, um Property-Werte zu setzen.
Mit Property-Werten können Ressourcen gefiltert werden. Filterung bedeutet in diesem
Zusammenhang, dass "${...}"-Platzhalterausdrücke durch Werte von Properties ersetzt
werden können.
In diesem Programmierbeispiel werden die Property-Werte lediglich in der Java-Anwendung
ausgelesen und angezeigt. Mit Property-Werten könnte aber auch der Build-Prozess gesteuert
werden (z.B. über Profile).
Alternativ könnten auch mit dem maven-replacer-plugin Ausdrücke ersetzt werden.
1. Öffnen Sie ein Kommandozeilenfenster, wechseln Sie in Ihr Projekte-Verzeichnis (z.B.
\MeinWorkspace) und erzeugen Sie eine neue Projektstruktur:
cd \MeinWorkspace
mvn archetype:generate -DinteractiveMode=false DgroupId=de.meinefirma.meinprojekt -DartifactId=MvnPropApp
cd MvnPropApp
md src\main\filters
md src\main\resources\filtered
md src\test\resources
2. Ersetzen Sie in src\main\java\de\meinefirma\meinprojekt den Inhalt von
App.java durch:
3.
4. package de.meinefirma.meinprojekt;
5.
6. import java.io.IOException;
7. import java.util.Properties;
8.
9. public class App
10. {
11.
public static void main( String[] args )
12.
{
13.
System.out.println( (new App()).run() );
14.
}
15.
16.
public String run()
17.
{
18.
19.
20.
21.
Properties props = new Properties();
try {
// Properties einlesen:
props.load( getClass().getResourceAsStream(
"/filtered/MeineProps.properties" ) );
22.
} catch( IOException ex ) {
23.
throw new RuntimeException( ex.getCause() );
24.
}
25.
// Properties zu String wandeln:
26.
String s = props.toString();
27.
if( s.length() > 2 ) { s = s.substring( 1, s.length() - 1 ); }
28.
return s.replace( ", ", "\n" );
29.
}
30. }
31. Ersetzen Sie in src\test\java\de\meinefirma\meinprojekt den Inhalt von
AppTest.java durch:
32.
33. package de.meinefirma.meinprojekt;
34.
35. import java.io.IOException;
36. import java.util.Properties;
37. import junit.framework.TestCase;
38.
39. public class AppTest extends TestCase
40. {
41.
public void testApp()
42.
{
43.
Properties propsApp = new Properties(), propsTst = new
Properties();
44.
try {
45.
propsApp.load( getClass().getResourceAsStream(
"/filtered/MeineProps.properties" ) );
46.
propsTst.load( getClass().getResourceAsStream(
"/MeineTestProps.properties" ) );
47.
} catch( IOException ex ) {
48.
throw new RuntimeException( ex.getCause() );
49.
}
50.
assertEquals( "MeinPomProp vor Merge", "PropAusPom",
propsApp.get( "MeinPomProp" ) );
51.
propsApp.putAll( propsTst );
52.
assertEquals( "MeinPomProp nach Merge", "PropAusTestProps",
propsApp.get( "MeinPomProp" ) );
53.
}
54. }
Bitte beachten Sie, dass Sie im Test sowohl auf die Ressourcen unter
src\main\resources als auch unter src\test\resources zugreifen können.
Wenn Sie wie im Beispiel gezeigt einen Merge durchführen, können Sie Properties für
den Testfall überschreiben.
55. Ersetzen Sie den Inhalt der pom.xml durch:
56.
57. <project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
58.
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
59.
<modelVersion>4.0.0</modelVersion>
60.
<groupId>de.meinefirma.meinprojekt</groupId>
61.
<artifactId>MvnPropApp</artifactId>
62.
<version>1.0-SNAPSHOT</version>
63.
<packaging>jar</packaging>
64.
<name>MvnPropApp</name>
65.
<url>http://www.meinefirma.de</url>
66.
<build>
67.
<filters>
68.
<filter>src/main/filters/filter.properties</filter>
69.
</filters>
70.
<resources>
71.
<resource>
72.
<filtering>true</filtering>
73.
<directory>src/main/resources</directory>
74.
<includes>
75.
<include>**/filtered/*</include>
76.
</includes>
77.
</resource>
78.
<resource>
79.
<filtering>false</filtering>
80.
<directory>src/main/resources</directory>
81.
<excludes>
82.
<exclude>**/filtered/*</exclude>
83.
</excludes>
84.
</resource>
85.
</resources>
86.
</build>
87.
<dependencies>
88.
<dependency>
89.
<groupId>junit</groupId>
90.
<artifactId>junit</artifactId>
91.
<version>4.12</version>
92.
<scope>test</scope>
93.
</dependency>
94.
</dependencies>
95.
<properties>
96.
<MeinProp>PropAusPom</MeinProp>
97.
<MeinPomProp>PropAusPom</MeinPomProp>
98.
</properties>
99. </project>
Beachten Sie bitte den <build>- und den <properties>-Block:
Im <properties>-Block werden zwei Property-Werte definiert.
Im <build>-Block wird eine zusätzlich zu berücksichtigende Filter-Datei definiert
(<filter>src/main/filters/filter.properties) und es wird Filterung aktiviert
(<filtering>true), aber nicht für alle Ressourcen, sondern nur für die im filteredUnterverzeichnis. Wählen Sie für Filterung besser nicht das gesamte RessourcenVerzeichnis: Vielleicht wollen Sie später Dateien hinzufügen, die nicht gefiltert werden
dürfen (z.B. Bilddateien).
Erzeugen Sie unterhalb von src\main ein Unterverzeichnis mit dem Namen
filters.
Erzeugen Sie in diesem Unterverzeichnis die Datei filter.properties mit folgendem
Inhalt:
100.
101.
102. MeinProp=PropAusFilter
103. MeinFilterProp=PropAusFilter
104.
Erzeugen Sie unterhalb von src\main ein Unterverzeichnis mit dem Namen
resources und darunter eines mit dem Namen filtered.
Erzeugen Sie in letzterem die Datei MeineProps.properties mit folgendem Inhalt:
105.
106. MeinPropsProp=PropAusMeineProps
107. MeinCmdProp=${MeinCmdProp}
108. MeinPomProp=${MeinPomProp}
109. MeinFilterProp=${MeinFilterProp}
110. MeinProp=${MeinProp}
111. pom.name=${pom.name}
112. pom.version=${pom.version}
113. pom.build.finalName=${pom.build.finalName}
114. pom.url=${pom.url}
115. settings.localRepository=${settings.localRepository}
116. basedir=${basedir}
117. os.arch=${os.arch}
118. os.name=${os.name}
119. java.version=${java.version}
120. user.home=${user.home}
121.
Erzeugen Sie unterhalb von src\test ein Unterverzeichnis mit dem Namen
resources und erzeugen Sie darin die Datei MeineTestProps.properties mit
folgendem Inhalt:
122.
123. MeinPomProp=PropAusTestProps
124.
Lassen Sie sich die Verzeichnisstruktur anzeigen:
cd \MeinWorkspace\MvnPropApp
tree /F
-->
[\MeinWorkspace\MvnPropApp]
|- [src]
|
|- [main]
|
|
|- [filters]
|
|
|
'- filter.properties
|
|
|- [java]
|
|
|
'- [de]
|
|
|
'- [meinefirma]
|
|
|
'- [meinprojekt]
|
|
|
'- App.java
|
|
'- [resources]
|
|
'- [filtered]
|
|
'- MeineProps.properties
|
'- [test]
|
|- [java]
|
|
'- [de]
|
|
'- [meinefirma]
|
|
'- [meinprojekt]
|
|
'- AppTest.java
|
'- [resources]
|
'- MeineTestProps.properties
'- pom.xml
125.
Um den Build durchzuführen und das entstandene Programm inklusive eines
Kommandozeilenparameters ("-D...") auszuführen, geben Sie im
Kommandozeilenfenster ein:
cd \MeinWorkspace\MvnPropApp
mvn clean package "-DMeinCmdProp=PropVonCmd"
java -cp target/MvnPropApp-1.0-SNAPSHOT.jar
de.meinefirma.meinprojekt.App
Sie erhalten eine Ausgabe ähnlich zu:
MeinPropsProp=PropAusMeineProps
MeinCmdProp=PropVonCmd
MeinPomProp=PropAusPom
MeinFilterProp=PropAusFilter
MeinProp=PropAusPom
pom.name=MvnPropApp
pom.version=1.0-SNAPSHOT
pom.build.finalName=MvnPropApp-1.0-SNAPSHOT
pom.url=http://www.meinefirma.de
settings.localRepository=C:\Users\User\.m2\repository
basedir=\MeinWorkspace\MvnPropApp
os.arch=amd64
os.name=Windows 10
java.version=1.8
user.home=C:\Users\User
Die Property-Werte stammen aus unterschiedlichen Quellen:






MeinPropsProp wurde in der MeineProps.properties-Propertiesdatei definiert.
MeinCmdProp wurde über den Kommandozeilenparameter DMeinCmdProp=PropVonCmd definiert.
MeinPomProp wurde in der pom.xml als zusätzliche Property definiert.
Die pom...-Properties sind in der pom.xml definierte Standardeinträge.
MeinFilterProp wurde in der filter.properties definiert.
MeinProp wird sowohl in der pom.xml als auch in der filter.properties
gesetzt: Wie man sieht, setzt sich die pom.xml durch. Wenn Sie diese Property
zusätzlich in der Kommandozeile setzen würden (mvn clean package "-
DMeinCmdProp=PropVonCmd" "-DMeinProp=PropVonCmd"),
würde sich diese
durchsetzen.




126.
settings.localRepository
hätte in einer der beiden settings.xml definiert
sein können (im Beispiel war sie nicht definiert und es wird der Default-Wert
angezeigt).
basedir wird von Maven gesetzt.
os.arch, os.name, java.version und user.home sind Java-Systemproperties.
Properties können für die Test-Phase überschrieben werden.
Um nur die JUnit-Tests auszuführen, geben Sie im Kommandozeilenfenster ein:
cd \MeinWorkspace\MvnPropApp
mvn clean test
Falls es Probleme beim Test gibt, sehen Sie sich die Dateien im target\surefire-reportsVerzeichnis an:
start target\surefire-reports\de.meinefirma.meinprojekt.AppTest.txt
start target\surefire-reports\TESTde.meinefirma.meinprojekt.AppTest.xml
Die XML-Datei enthält auch relevante Environment-Variablen und Konfigurationen
(z.B. <property name="localRepository"
value="C:\Users\User\.m2\repository"/>).
127.
Sehen Sie nach, was in der resultierenden Jar-Datei enthalten ist:
cd \MeinWorkspace\MvnPropApp
mvn package "-DMeinCmdProp=PropVonCmd"
jar tvf target/MvnPropApp-1.0-SNAPSHOT.jar
-->
de/meinefirma/meinprojekt/App.class
filtered/MeineProps.properties
META-INF/MANIFEST.MF
META-INF/maven/de.meinefirma.meinprojekt/MvnPropApp/pom.properties
META-INF/maven/de.meinefirma.meinprojekt/MvnPropApp/pom.xml
Bitte beachten Sie: Die filter.properties-Datei wurde zwar beim Filtern
berücksichtigt (${MeinFilterProp} wurde umgewandelt in PropAusFilter), aber sie
wird nicht dem Ergebnis-Artefakt hinzugefügt.
Erweiterung um Maven-Profile
Ein profile definiert Voreinstellungen. Über Umgebungsbedingungen, Kommandozeilenoptionen
oder andere Parameter können Profile aktiviert werden. Eine Einführung finden Sie unter
Introduction to Build Profiles. Ein anderes Profile verwendendes Beispiel finden Sie unter
Integrationstest mit Cargo.
Außer in der pom.xml können Profile auch in einer settings.xml definiert werden. Unter
Maven 2.2.1 war zusätzlich auch die Definition von Profilen in einer profiles.xml-Datei
möglich, was ab Maven 3.0 nicht mehr möglich ist (siehe Maven Release Notes und Maven 3.x
Compatibility Notes).
In der settings.xml definierte Profile beeinflussen hauptsächlich:


Properties können definiert oder umdefiniert werden.
Repositories können zusätzlich definiert werden.
Am meisten Einflussmöglichkeiten gibt es, wenn das Profil in der pom.xml definiert wird. Dann
sind beispielsweise zusätzlich beeinflussbar:




Plugin-Goals können zusätzlich ausgeführt werden.
Module können unterschiedlich gewählt werden.
Dependencies können ergänzt werden.
Dependency-Management und Distribution-Management können beeinflusst werden.
Die Auswahl und Aktivierung (activation) eines (oder mehrerer) Profile kann über
verschiedene Auslöser erfolgen:






Explizit durch direkte Auswahl eines oder mehrerer Profile über die -PKommandozeilenoption (z.B. -P MeinProfil1,MeinProfil2), oder auch der
Ausschluss eines Profiles (z.B. -P !MeinProfil3)
Kommandozeilen-Property (z.B. -DMeinProp=MeinWert)
Umgebungsvariable
System-Properties (z.B. Betriebssystem, Java-Version, ...)
Existenz eines Properties, ein bestimmter Wert eines Properties oder die Negation, also
das Property hat nicht einen bestimmten Wert (<property><name>...<value>...)
Existenz oder Fehlen einer Datei (<file><exists>... bzw. <file><missing>...)
Normalerweise werden die verschiedenen Profile eines Projekts in nur einer der möglichen
Dateien gespeichert, fast immer in der pom.xml.
Von den vielen Möglichkeiten kann das folgende Beispiel nur wenig demonstrieren: Die ProfileAktivierung erfolgt über -P-Kommandos oder alternativ über Properties, die per
Umgebungsvariable oder Kommandozeilenparameter gesetzt werden. Und als Effekt werden
lediglich die eingestellten Properties ausgegeben.
Damit die steuernde Property auch per Umgebungsvariable gesetzt werden kann, muss sie mit
einem vorangestellten "env." beginnen und ansonsten in Großbuchstaben geschrieben sein (im
Beispiel lautet sie: "env.UMGEBUNG"). Als Umgebungsvariable wird sie ohne das "env." gesetzt
(siehe Beispiel unten).
1. Sie können dieses und die meisten der folgenden Programmierbeispiele wahlweise
downloaden oder, wie im Folgenden beschrieben wird, schrittweise ausführen.
2. Voraussetzung ist, dass Sie das obige Beispiel Maven-Properties-Projekt (im Folgenden
"MvnPropApp" genannt) erfolgreich durchgeführt haben.
3. Ergänzen Sie im MvnPropApp-Projektverzeichnis in der Datei pom.xml zwischen
</properties> und </project> Folgendes:
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
<profiles>
<profile>
<id>Entw</id>
<activation>
<property>
<name>env.UMGEBUNG</name>
<value>Entwicklung</value>
</property>
</activation>
<properties>
<appsrv.url>meine.Url.zum.lokalen.Appserver</appsrv.url>
<appsrv.usr>mein lokaler AppServer-User</appsrv.usr>
<appsrv.pwd>geheim</appsrv.pwd>
<db.url>meine.Url.zur.lokalen.Datenbank</db.url>
<db.usr>mein lokaler DB-User</db.usr>
<db.pwd>supergeheim</db.pwd>
</properties>
</profile>
<profile>
<id>IntTest</id>
<activation>
<property>
<name>env.UMGEBUNG</name>
<value>Integrationstest</value>
</property>
</activation>
<properties>
<appsrv.url>Url.zum.IntTest.Appserver</appsrv.url>
<appsrv.usr>IntTest-AppServer-User</appsrv.usr>
<appsrv.pwd>geheim</appsrv.pwd>
<db.url>Url.zur.IntTest.Datenbank</db.url>
<db.usr>IntTest-DB-User</db.usr>
<db.pwd>supergeheim</db.pwd>
</properties>
</profile>
</profiles>
41. Ergänzen Sie in der Properties-Datei
src\main\resources\filtered\MeineProps.properties
42.
43. env.UMGEBUNG=${env.UMGEBUNG}
44. appsrv.url=${appsrv.url}
45. appsrv.usr=${appsrv.usr}
Folgendes:
46.
47.
48.
49.
appsrv.pwd=${appsrv.pwd}
db.url=${db.url}
db.usr=${db.usr}
db.pwd=${db.pwd}
50. Öffnen Sie ein Kommandozeilenfenster, wechseln Sie in das MvnPropAppProjektverzeichnis und führen Sie folgende Kommandos aus:
cd \MeinWorkspace\MvnPropApp
mvn clean package
java -cp target/MvnPropApp-1.0-SNAPSHOT.jar
de.meinefirma.meinprojekt.App | sort
--> Die appsrv...- und db...-Properties sind nicht gesetzt.
set UMGEBUNG=Entwicklung
mvn clean package
java -cp target/MvnPropApp-1.0-SNAPSHOT.jar
de.meinefirma.meinprojekt.App | sort
--> Die appsrv...- und db...-Properties sind für "Entwicklung" gesetzt.
mvn clean package "-Denv.UMGEBUNG=Integrationstest"
java -cp target/MvnPropApp-1.0-SNAPSHOT.jar
de.meinefirma.meinprojekt.App | sort
--> Die appsrv...- und db...-Properties sind für "Integrationstest" gesetzt
(die set-Umgebungsvariable wird durch den Kommandozeilenparameter
überschrieben).
Statt des Kommandozeilenparameters können Sie auch die Umgebungsvariable setzen:
set UMGEBUNG=Integrationstest
mvn clean package
java -cp target/MvnPropApp-1.0-SNAPSHOT.jar
de.meinefirma.meinprojekt.App | sort
Sie erhalten eine Ausgabe wie oben gezeigt, aber je nach gesetztem env.UMGEBUNGProperty-Wert ergänzt um beispielsweise:
appsrv.pwd=geheim
appsrv.url=Url.zum.IntTest.Appserver
appsrv.usr=IntTest-AppServer-User
db.pwd=supergeheim
db.url=Url.zur.IntTest.Datenbank
db.usr=IntTest-DB-User
env.UMGEBUNG=Integrationstest
51. Alternativ können Sie die verschiedenen Profile auch über die -P-Option aktivieren:
set UMGEBUNG=
mvn package
java -cp target/MvnPropApp-1.0-SNAPSHOT.jar
de.meinefirma.meinprojekt.App | sort
--> Die appsrv...- und db...-Properties sind nicht gesetzt.
mvn -P IntTest package
java -cp target/MvnPropApp-1.0-SNAPSHOT.jar
de.meinefirma.meinprojekt.App | sort
--> Die appsrv...- und db...-Properties sind für "Integrationstest" gesetzt.
52. Falls Sie viele Profile haben und die Übersicht verlieren, können Sie sich mit dem Maven
Help Plugin die aktivierten anzeigen lassen:
Wenn Sie
mvn help:active-profiles
ohne Profilangabe aufrufen, wird kein Profil aufgelistet.
Aber sowohl
mvn help:active-profiles "-Denv.UMGEBUNG=Integrationstest"
als auch
mvn help:active-profiles -P IntTest
führen zu:
The following profiles are active:
- IntTest (source: ...)
53. Das Maven Help Plugin bietet noch weitere hilfreiche Kommandos, zum Beispiel:
mvn help:effective-pom "-Denv.UMGEBUNG=Integrationstest"
Generierung von HTML, PDF, RTF etc. mit DocBook und dem Docbkx Maven
Plugin
1. Sehen Sie sich die Doku zu DocBook und Docbkx an:
http://www.docbook.org/tdg5/en/html/docbook.html
http://docs.codehaus.org/display/MAVENUSER/Docbkx+Maven+Plugin
2. Öffnen Sie ein Kommandozeilenfenster, wechseln Sie in Ihr Projekte-Verzeichnis (z.B.
\MeinWorkspace) und erzeugen Sie eine neue Projektstruktur:
cd \MeinWorkspace
md MvnDocbkx\src\docbkx
cd MvnDocbkx
3. Erzeugen Sie im MvnDocbkx-Projektverzeichnis die Projektkonfiguration: pom.xml
4.
5. <project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
6.
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
7.
<modelVersion>4.0.0</modelVersion>
8.
<groupId>de.meinefirma.meinprojekt</groupId>
9.
<artifactId>MvnDocbkx</artifactId>
10.
<version>1.0</version>
11.
<packaging>pom</packaging>
12.
<name>MvnDocbkx</name>
13.
<build>
14.
<plugins>
15.
<plugin>
16.
<groupId>com.agilejava.docbkx</groupId>
17.
<artifactId>docbkx-maven-plugin</artifactId>
18.
<version>2.0.14</version>
19.
<executions>
20.
<execution>
21.
<goals>
22.
<goal>generate-html</goal>
23.
<goal>generate-webhelp</goal>
24.
<goal>generate-rtf</goal>
25.
<goal>generate-pdf</goal>
26.
</goals>
27.
<phase>package</phase>
28.
</execution>
29.
</executions>
30.
<configuration>
31.
<includes>book.xml</includes>
32.
</configuration>
33.
</plugin>
34.
</plugins>
35.
</build>
36. </project>
37. Erzeugen Sie im src\docbkx-Verzeichnis die Dokumentbeschreibung: book.xml
38.
39. <book xmlns="http://docbook.org/ns/docbook"
40.
xmlns:xlink="http://www.w3.org/1999/xlink"
41.
version="5.0">
42.
<info>
43.
<author>
44.
<personname><firstname>Torsten</firstname><surname>Horn</surname></pers
onname>
45.
</author>
46.
<copyright><year>2012</year><holder>Torsten
Horn</holder></copyright>
47.
</info>
48.
<title>Mein DocBook-Dokument</title>
49.
<article>
50.
<title>Mein erstes Kapitel</title>
51.
<section>
52.
<title>Mein erster Abschnitt</title>
53.
<para>Mein erster Absatz vom ersten Abschnitt.</para>
54.
<para>Mein zweiter Absatz vom ersten Abschnitt.</para>
55.
</section>
56.
<section>
57.
<title>Mein zweiter Abschnitt</title>
58.
<para>Mein erster Absatz vom zweiten Abschnitt.</para>
59.
<para>Mein zweiter Absatz vom zweiten Abschnitt, mit
Formatierungen, z.B.:
60.
<quote>quote</quote>, <emphasis>emphasis</emphasis>,
<replaceable>replaceable</replaceable>,
61.
<literal>literal</literal>,
<userinput>userinput</userinput>.</para>
62.
</section>
63.
</article>
64.
<article>
65.
<title>Mein zweites Kapitel</title>
66.
<section>
67.
<title>Mein neuer Abschnitt im zweiten Kapitel</title>
68.
<para>Mein erster Absatz vom neuen Abschnitt im zweiten
Kapitel.</para>
69.
<para>Mein zweiter Absatz vom neuen Abschnitt im zweiten Kapitel
mit einer Tabelle und mit HTML-Links:</para>
70.
<table>
71.
<title>Doku-Tabelle</title>
72.
<tgroup cols="2">
73.
<thead><row><entry>Thema</entry><entry>DokuLink</entry></row></thead>
74.
<tbody>
75.
<row><entry>DocBook</entry><entry><link
xlink:href="http://www.docbook.org/tdg5/en/html/docbook.html">http://ww
w.docbook.org/tdg5/en/html/docbook.html</link></entry></row>
76.
<row><entry>Docbkx </entry><entry><link
xlink:href="http://docs.codehaus.org/display/MAVENUSER/Docbkx+Maven+Plu
gin">http://docs.codehaus.org/display/MAVENUSER/Docbkx+Maven+Plugin</li
nk></entry></row>
77.
</tbody>
78.
</tgroup>
79.
</table>
80.
</section>
81.
</article>
82. </book>
83. Führen Sie im MvnDocbkx-Projektverzeichnis
aus:
cd \MeinWorkspace\MvnDocbkx
mvn clean package
start target/docbkx/html/book.html
start target/docbkx/webhelp/book/content/index.html
start target/docbkx/pdf/book.pdf
start target/docbkx/rtf/book.rtf
84. Das Layout der Dokumente ist in diesem einfachen Beispiel sehr rudimentär, aber Sie
finden in der oben genannten Doku viele Hinweise zur Verbesserung.
85. Falls es Ihnen um PDF-Generierung geht, sehen Sie sich auch an:
PDF mit Apache PDFBox
PDF mit XSL-FO und Apache FOP
Java-Codegenerierung aus einem XSD-Schema mit JAXB
1. Das folgende Beispiel demonstriert Verschiedenes:
 Aus einer XML-Schema-Definitions-Datei (XSD-Datei) wird Java-Code
generiert.
Falls Sie sich nicht mit XML-Schema-Definitionen auskennen: Sehen Sie sich die
Einführungen unter java-xsd.htm und http://de.wikipedia.org/wiki/XML_Schema
an sowie die W3C XML Schema Specification entweder im englischen Original
oder ins deutsche übersetzt unter Einführung, Strukturen und Datentypen.
 Als Codegenerator wird JAXB 2 verwendet. Siehe hierzu: Generierung von JavaCode aus einem XSD-Schema, JSR 222 und JAXB 2.x.
 Die Einbindung in Maven erfolgt über das maven-jaxb2-plugin.
 Da JAXB 2 mindestens Java 5 voraussetzt, wird außerdem gezeigt, wie dies dem
maven-compiler-plugin mitgeteilt wird (über <configuration>...1.7...).
2. Öffnen Sie ein Kommandozeilenfenster, wechseln Sie in Ihr Projekte-Verzeichnis (z.B.
\MeinWorkspace) und erzeugen Sie eine neue Projektstruktur:
cd \MeinWorkspace
mvn archetype:generate -DinteractiveMode=false DgroupId=de.meinefirma.meinprojekt -DartifactId=MvnJaxbApp
cd MvnJaxbApp
md src\main\resources
tree /F
3. Ersetzen Sie den Inhalt der pom.xml durch:
4.
5. <project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
6.
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
7.
<modelVersion>4.0.0</modelVersion>
8.
<groupId>de.meinefirma.meinprojekt</groupId>
9.
<artifactId>MvnJaxbApp</artifactId>
10.
<version>1.0-SNAPSHOT</version>
11.
<packaging>jar</packaging>
12.
<name>MvnJaxbApp</name>
13.
<build>
14.
<plugins>
15.
<plugin>
16.
<groupId>org.jvnet.jaxb2.maven2</groupId>
17.
<artifactId>maven-jaxb2-plugin</artifactId>
18.
<version>0.13.2</version>
19.
<executions>
20.
<execution>
21.
<goals>
22.
<goal>generate</goal>
23.
</goals>
24.
<configuration>
25.
<generatePackage>de.meinefirma.meinprojekt.generated</generatePackage>
26.
</configuration>
27.
</execution>
28.
</executions>
29.
</plugin>
30.
<plugin>
31.
<groupId>org.apache.maven.plugins</groupId>
32.
<artifactId>maven-compiler-plugin</artifactId>
33.
<version>3.6.1</version>
34.
<configuration>
35.
<source>1.7</source>
36.
<target>1.7</target>
37.
</configuration>
38.
</plugin>
39.
</plugins>
40.
</build>
41.
<dependencies>
42.
<dependency>
43.
<groupId>com.sun.xml.bind</groupId>
44.
<artifactId>jaxb-impl</artifactId>
45.
<version>2.2.11</version>
46.
</dependency>
47.
<dependency>
48.
<groupId>junit</groupId>
49.
<artifactId>junit</artifactId>
50.
<version>4.12</version>
51.
<scope>test</scope>
52.
</dependency>
53.
</dependencies>
54. </project>
55. Ersetzen Sie den Inhalt von
src\test\java\de\meinefirma\meinprojekt\AppTest.java
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
durch:
package de.meinefirma.meinprojekt;
import
import
import
import
import
de.meinefirma.meinprojekt.generated.Buch;
de.meinefirma.meinprojekt.generated.Buecherliste;
java.math.BigDecimal;
org.junit.Test;
static org.junit.Assert.assertEquals;
public class AppTest
{
@Test public void testApp()
{
Buch bu = new Buch();
bu.setTitel( "Mein Java-Buch" );
bu.setJahr( 2009 );
bu.setPreis( new BigDecimal( "47.11" ) );
bu.setKommentar( "Super!" );
Buecherliste bl = new Buecherliste();
bl.setKategorie( "Java-Bücher" );
bl.getListe().add( bu );
assertEquals( "Buecherliste", 1, bl.getListe().size() );
}
}
82. Erzeugen Sie unterhalb von src\main ein Unterverzeichnis mit dem Namen resources
und erzeugen Sie darin die XSD-Datei buecherliste.xsd mit folgendem Inhalt:
83.
84. <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
85.
86.
<xsd:complexType name="Buecherliste">
87.
<xsd:sequence>
88.
<xsd:element name="kategorie" type="xsd:string" />
89.
<xsd:element name="liste"
type="Buch" maxOccurs="unbounded"
/>
90.
</xsd:sequence>
91.
</xsd:complexType>
92.
93.
<xsd:complexType name="Buch">
94.
<xsd:sequence>
95.
<xsd:element name="titel"
type="xsd:string" />
96.
<xsd:element name="jahr"
type="xsd:int"
/>
97.
<xsd:element name="preis"
type="xsd:decimal" />
98.
<xsd:element name="kommentar" type="xsd:string" />
99.
</xsd:sequence>
100.
<xsd:attribute name="id"
type="xsd:long"
/>
101.
</xsd:complexType>
102.
103. </xsd:schema>
104.
105.
106.
107.
108.
109.
110.
111.
112.
113.
114.
115.
116.
117.
118.
119.
120.
121.
122.
123.
Sehen Sie sich mit tree /F die Verzeichnisstruktur an:
[\MeinWorkspace\MvnJaxbApp]
|- [src]
|
|- [main]
|
|
|- [java]
|
|
|
'- [de]
|
|
|
'- [meinefirma]
|
|
|
'- [meinprojekt]
|
|
|
'- App.java
|
|
'- [resources]
|
|
'- buecherliste.xsd
|
'- [test]
|
'- [java]
|
'- [de]
|
'- [meinefirma]
|
'- [meinprojekt]
|
'- AppTest.java
'- pom.xml
Bauen Sie das Projekt und sehen Sie sich das Ergebnis im target-Verzeichnis an:
cd \MeinWorkspace\MvnJaxbApp
mvn package
tree /F
Sie erhalten:
[\MeinWorkspace\MvnJaxbApp]
|- [src]
|
|- [main]
|
|
|- [java]
|
|
|
'- [de]
|
|
|
'- [meinefirma]
|
|
|
'- [meinprojekt]
|
|
|
'- App.java
|
|
'- [resources]
|
|
'- buecherliste.xsd
|
'- [test]
|
'- [java]
|
'- [de]
|
'- [meinefirma]
|
'- [meinprojekt]
|
'- AppTest.java
|- [target]
|
|- ...
|
|- [generated-sources]
|
|
'- [xjc]
|
|
|- [de]
|
|
|
'- [meinefirma]
|
|
|
'- [meinprojekt]
|
|
|
'- [generated]
|
|
|
|- Buch.java
|
|
|
|
|
|
|
'- ...
'- pom.xml
|
|
'- ...
|- Buecherliste.java
'- ObjectFactory.java
Aus der XSD-Schema-Definition buecherliste.xsd im src\main\resourcesVerzeichnis wurden drei Java-Klassen im target\generatedsources\xjc\de\meinefirma\meinprojekt\generated-Verzeichnis generiert:
Buch.java, Buecherliste.java und ObjectFactory.java. Sehen Sie sich diese drei
Klassen an und beachten Sie, wie in der Buecherliste die Liste deklariert ist (über
maxOccurs="unbounded").
Wie Sie diese Klassen ganz normal in Ihren Programmen verwenden können, zeigt der
JUnit-Test in AppTest.java.
REST-Webservice mit JAX-RS und SOAP-Webservice mit JAX-WS
REST-Webservice mit JAX-RS
Beispiele für RESTful-Webservices mit JAX-RS finden Sie unter jee-rest.htm#JaxRsMitMaven.
SOAP-Webservice mit JAX-WS
Das folgende Beispiel zeigt die Implementierung eines SOAP-Webservices mit JAX-WS
inklusive Server und Client, allerdings nur in einer Quick-and-Dirty-Minimalversion.
Weiterführende Hinweise finden Sie unter jee-jax-ws.htm. Wenn Sie mehr Features von JAXWS nutzen wollen, sollten Sie sich das JAX-WS Maven-Plugin ansehen. Wenn Sie die
Webservice-Kommunikation beobachten wollen, empfiehlt sich soapUI.
1. Öffnen Sie ein Kommandozeilenfenster, wechseln Sie in Ihr Projekte-Verzeichnis (z.B.
\MeinWorkspace) und erzeugen Sie eine neue Projektstruktur:
cd \MeinWorkspace
md MvnJaxWs\src\main\java\de\meinefirma\meinprojekt
cd MvnJaxWs
2. Erstellen Sie im MvnJaxWs-Projektverzeichnis die Projektkonfiguratiosdatei: pom.xml
3.
4. <project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
5.
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
6.
<modelVersion>4.0.0</modelVersion>
7.
<groupId>de.meinefirma.meinprojekt</groupId>
8.
<artifactId>MvnJaxWs</artifactId>
9.
<version>1.0-SNAPSHOT</version>
10.
<packaging>jar</packaging>
11.
<name>MvnJaxWs</name>
12.
<build>
13.
<plugins>
14.
<plugin>
15.
<groupId>org.apache.maven.plugins</groupId>
16.
<artifactId>maven-compiler-plugin</artifactId>
17.
<version>3.6.1</version>
18.
<configuration>
19.
<source>1.7</source>
20.
<target>1.7</target>
21.
</configuration>
22.
</plugin>
23.
</plugins>
24.
</build>
25.
<dependencies>
26.
<dependency>
27.
<groupId>javax.xml.ws</groupId>
28.
<artifactId>jaxws-api</artifactId>
29.
<version>2.2.11</version>
30.
</dependency>
31.
</dependencies>
32. </project>
Seit Java SE 6 können Sie den <dependency>...jaxws-api...-Abschnitt auch
weglassen.
33. Legen Sie im Verzeichnis src\main\java\de\meinefirma\meinprojekt die folgenden
vier Java-Dateien an:
HalloWelt.java
package de.meinefirma.meinprojekt;
import javax.jws.*;
@WebService
public interface HalloWelt
{
public String hallo( @WebParam( name = "wer" ) String wer );
}
HalloWeltImpl.java
package de.meinefirma.meinprojekt;
import javax.jws.WebService;
@WebService( endpointInterface="de.meinefirma.meinprojekt.HalloWelt" )
public class HalloWeltImpl implements HalloWelt
{
@Override
public String hallo( String wer )
{
return "Hallo " + wer;
}
}
TestWsServer.java
package de.meinefirma.meinprojekt;
import javax.xml.ws.Endpoint;
public class TestWsServer
{
public static void main( final String[] args )
{
String url = ( args.length > 0 ) ? args[0] :
"http://localhost:8080/test";
Endpoint.publish( url, new HalloWeltImpl() );
}
}
TestWsClient.java
package de.meinefirma.meinprojekt;
import java.net.URL;
import javax.xml.namespace.QName;
import javax.xml.ws.Service;
public class TestWsClient
{
public static void main( final String[] args ) throws Throwable
{
String url = ( args.length > 0 ) ? args[0] :
"http://localhost:8080/test";
Service service = Service.create(
new URL( url + "?wsdl" ),
new QName( "http://meinprojekt.meinefirma.de/",
"HalloWeltImplService" ) );
HalloWelt halloWelt = service.getPort( HalloWelt.class );
System.out.println( "\n" + halloWelt.hallo( args.length > 1 ?
args[1] : "" ) );
}
}
34. Die simplifizierte Projektstruktur sieht so aus:
cd \MeinWorkspace\MvnJaxWs
tree /F
[\MeinWorkspace\MvnJaxWs]
|- [src]
|
'- [main]
|
'- [java]
|
'- [de]
|
'- [meinefirma]
|
'- [meinprojekt]
|
|- HalloWelt.java
|
|- HalloWeltImpl.java
|
|- TestWsClient.java
|
'- TestWsServer.java
'- pom.xml
35. Öffnen Sie ein Kommandozeilenfenster, bauen Sie das Projekt und starten Sie den Server
mit dem Webservice:
cd \MeinWorkspace\MvnJaxWs
mvn clean package
java -cp target/MvnJaxWs-1.0-SNAPSHOT.jar
de.meinefirma.meinprojekt.TestWsServer http://localhost:8080/test
36. Öffnen Sie ein zweites Kommandozeilenfenster und starten Sie den Webservice-Client:
cd \MeinWorkspace\MvnJaxWs
java -cp target/MvnJaxWs-1.0-SNAPSHOT.jar
de.meinefirma.meinprojekt.TestWsClient http://localhost:8080/test ich
Sie erhalten:
Hallo ich
37. Sehen Sie sich die generierte WSDL-Datei über http://localhost:8080/test?wsdl und die
generierte Schema-XSD-Datei über http://localhost:8080/test?xsd=1 an.
Falls Ihr Webbrowser nichts anzeigt, verwenden Sie curl:
curl http://localhost:8080/test?wsdl
curl http://localhost:8080/test?xsd=1
38. Sie können auch andere URLs beim TestWsServer- und TestWsClient-Aufruf
übergeben, beispielsweise http://localhost:4711/xyz.
39. Beenden Sie den Webservice-Server mit "Strg + C".
40. Sehen Sie sich die Webservice-Kommunikation mit soapUI an (siehe soapUIScreenshot).
FTP-Client und embedded FTP-Server im JUnit-Test
Mehrere Beispiele zu FTP-Projekten mit Maven finden Sie in java-ftp.htm.
Einfaches Multimodulprojekt
Größere Projekte bestehen aus vielen Teilprojekten. Um alle Teilprojekte gemeinsam
kompilieren, bauen und verwalten zu können, werden sie zu Multimodulprojekten
zusammengefasst (auch Multiprojekt genannt). Dies wird im Folgenden demonstriert. In diesem
Dokument werden eher einfache Aspekte von Maven-Multimodulprojekten besprochen.
Weitergehende Betrachtungen zum Zusammenspiel mit Eclipse und DVCS (z.B. Mercurial)
finden Sie unter: maven-multiproj.htm.
1. Voraussetzung ist, dass Sie die beiden obigen Beispiele Maven-Webapp-Projekt
(MvnJettyWebApp) und Maven-Properties-Projekt (MvnPropApp, egal ob mit oder ohne
Erweiterung um Maven-Profile) erfolgreich durchgeführt haben.
(Alternativ können Sie auch die genannten Projekte oder das Multimodulprojekt
downloaden.)
2. Kopieren Sie die beiden Projekte MvnPropApp und MvnJettyWebApp in neue "...-Mp1"Verzeichnisse und erstellen Sie für das neue Multimodulprojekt das neue Verzeichnis
MvnMultiProj1:
cd \MeinWorkspace
xcopy MvnPropApp MvnPropApp-Mp1\ /S
xcopy MvnJettyWebApp MvnJettyWebApp-Mp1\ /S
md MvnMultiProj1
3. Wechseln Sie in das MvnPropApp-Mp1-Verzeichnis und überprüfen Sie, dass das Projekt
weiterhin funktioniert:
cd \MeinWorkspace\MvnPropApp-Mp1
mvn clean package "-DMeinCmdProp=PropVonCmd"
java -cp target/MvnPropApp-1.0-SNAPSHOT.jar
de.meinefirma.meinprojekt.App | sort
Wechseln Sie in das MvnJettyWebApp-Mp1-Verzeichnis und überprüfen Sie, dass auch
dieses Projekt weiterhin funktioniert:
cd \MeinWorkspace\MvnJettyWebApp-Mp1
mvn package jetty:run
Sehen Sie sich die Webseite an:
start http://localhost:8080/MvnJettyWebApp
Beenden Sie Jetty mit "Strg + C".
4. Bislang sind die beiden Projekte MvnPropApp-Mp1 und MvnJettyWebApp-Mp1
unabhängig voneinander.
Multimodulprojekte machen insbesondere bei voneinander abhängigen Projekten Sinn.
Deshalb ändern wir MvnJettyWebApp-Mp1 so, dass es MvnPropApp-Mp1 benötigt.
Ersetzen Sie im Verzeichnis MvnJettyWebApp-Mp1\src\main\webapp den Inhalt der
Datei index.jsp durch:
<%@ page import="java.text.SimpleDateFormat" %>
<%@ page import="java.util.Date" %>
<html>
<head><title>MvnMultiProj-JSP</title></head>
<body>
<h2>MvnMultiProj-JSP</h2>
<%= request.getRemoteHost() %><br>
<%= (new SimpleDateFormat("yyyy-MM-dd, HH:mm:ss")).format(new Date()) +
" h" %><br><br>
<%= (new de.meinefirma.meinprojekt.App()).run().replace( "\n", "<br>\n"
) %>
</body>
</html>
5. Die Abhängigkeit zu MvnPropApp-Mp1 müssen Sie in der pom.xml von
MvnJettyWebApp-Mp1 eintragen. Ergänzen Sie im MvnJettyWebApp-Mp1-Verzeichnis die
pom.xml um folgenden <dependencies>-Block:
6.
7.
8.
9.
10.
11.
12.
13.
14.
<dependencies>
<dependency>
<groupId>de.meinefirma.meinprojekt</groupId>
<artifactId>MvnPropApp</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
Wenn Sie jetzt MvnJettyWebApp-Mp1 ausführen, erhalten Sie eine
cd \MeinWorkspace\MvnJettyWebApp-Mp1
mvn package jetty:run
Fehlermeldung:
-->
[ERROR] Failed to execute goal on project MvnJettyWebApp:
Could not resolve dependencies for project
de.meinefirma.meinprojekt:MvnJettyWebApp:war:1.0-SNAPSHOT:
Could not find artifact de.meinefirma.meinprojekt:MvnPropApp:jar:1.0SNAPSHOT
(Falls Sie die Fehlermeldung nicht erhalten, haben Sie wahrscheinlich das Kommando
"mvn install" schon mal getestet. Bitte löschen Sie in diesem Fall den Inhalt des
Verzeichnisses ${localRepository}\de\meinefirma\meinprojekt.)
15. Wir könnten jetzt im MvnPropApp-Mp1-Projekt das Kommando "mvn install"
ausführen. Dann würde das MvnPropApp-.jar-Artefakt in das lokale Maven-Repository
kopiert und es würde vom MvnJettyWebApp-Mp1-Projekt gefunden werden.
Diese Schritte müssten aber bei Änderungen jeweils wiederholt werden, was spätestens
dann nicht mehr sinnvoll durchführbar und verwaltbar ist, wenn Sie nicht nur zwei,
sondern viel mehr Teilprojekte haben.
Deshalb erstellen wir ein übergeordnetes Multimodulprojekt.
Erstellen Sie im MvnMultiProj1-Verzeichnis eine neue pom.xml, in der alle Teilprojekte
eingetragen werden (im Beispiel MvnPropApp-Mp1 und MvnJettyWebApp-Mp1):
16.
17. <project>
18.
<modelVersion>4.0.0</modelVersion>
19.
<groupId>de.meinefirma.meinprojekt</groupId>
20.
<artifactId>MvnMultiProj1</artifactId>
21.
<version>1.0</version>
22.
<packaging>pom</packaging>
23.
<name>MvnMultiProj1</name>
24.
<modules>
25.
<module>../MvnJettyWebApp-Mp1</module>
26.
<module>../MvnPropApp-Mp1</module>
27.
</modules>
28. </project>
29. Die drei Projekte mit ihren jeweiligen pom.xml sind jetzt so
30.
31. [\MeinWorkspace]
32.
|- [MvnJettyWebApp-Mp1]
33.
|
|- [src]
34.
|
|
'- ...
35.
|
'- pom.xml
36.
|- [MvnMultiProj1]
37.
|
'- pom.xml
38.
'- [MvnPropApp-Mp1]
39.
|- [src]
40.
|
'- ...
41.
'- pom.xml
angeordnet:
42. Jetzt können Sie mit folgenden Kommandos alle Teilprojekte (egal wie viele)
kompilieren, alle Artefakte erzeugen und die Anwendung starten:
cd \MeinWorkspace\MvnMultiProj1
mvn install
cd \MeinWorkspace\MvnJettyWebApp-Mp1
mvn jetty:run
Die Webseite des MvnJettyWebApp-Mp1-Projekts zeigt das Ergebnis des MvnPropAppMp1-Projekts an:
start http://localhost:8080/MvnJettyWebApp
43. Falls Unterprojekte (wie MvnPropApp-Mp1 und MvnJettyWebApp-Mp1) vom
übergeordneten Basisprojekt (MvnMultiProj1) Einstellungen erben sollen, können Sie
das übergeordnete Basisprojekt als <parent> in den pom.xml-Dateien der Unterprojekte
eintragen:
44.
45.
46.
47.
48.
49.
50.
<parent>
<groupId>de.meinefirma.meinprojekt</groupId>
<artifactId>MvnMultiProj1</artifactId>
<version>1.0</version>
<relativePath>../MvnMultiProj1/pom.xml</relativePath>
</parent>
Dies wird im folgenden Beispiel demonstriert.
Multimodulprojekt mit Plugin-Management und Dependency-Management
Bei Unterprojekten sollten gemeinsame Einstellungen (z.B. Java-Versionen und Versionen von
Libs) nicht in jeder pom.xml jedes einzelnen Unterprojekts vorgenommen werden, sondern
zentral erfolgen. Dies hat zwei Vorteile:
- Die pom.xml der einzelnen Unterprojekte sind kürzer und fehlerfreier.
- Änderungen können an einer Stelle für alle Unterprojekte durchgeführt werden.
Bitte beachten Sie, dass durch die im Folgenden vorgestellten <pluginManagement>- und
<dependencyManagement>-Blöcke noch keine Abhängigkeiten erzeugt werden, sondern vorerst
lediglich Deklarationen und Konfigurationen erfolgen (z.B. zur Versionsnummer, zum Scope
und <configuration>-Einstellungen).
Ein noch etwas ausgefeilteres Multimodulprojektbeispiel finden Sie unter Java EE mit EJB3.
1. Voraussetzung für das folgende Beispiel ist, dass Sie die Beispiele JAX-WS (MvnJaxWs)
und Einfaches Multimodulprojekt (MvnMultiProj1) erfolgreich durchgeführt haben.
(Alternativ können Sie die Projekte auch downloaden.)
2. Kopieren Sie die drei Unterprojekte in neue Verzeichnisse und erstellen Sie für das neue
Multimodulprojekt das neue Verzeichnis MvnMultiProj2:
cd \MeinWorkspace
xcopy MvnJaxWs MvnJaxWs-Mp2\ /S
xcopy MvnJettyWebApp-Mp1 MvnJettyWebApp-Mp2\ /S
xcopy MvnPropApp-Mp1 MvnPropApp-Mp2\ /S
md MvnMultiProj2
cd MvnMultiProj2
3. Erzeugen Sie im MvnMultiProj2-Verzeichnis folgende pom.xml:
4.
5. <project>
6.
<modelVersion>4.0.0</modelVersion>
7.
<groupId>de.meinefirma.meinprojekt</groupId>
8.
<artifactId>MvnMultiProj2</artifactId>
9.
<version>1.0</version>
10.
<packaging>pom</packaging>
11.
<name>MvnMultiProj2</name>
12.
<modules>
13.
<module>../MvnJaxWs-Mp2</module>
14.
<module>../MvnJettyWebApp-Mp2</module>
15.
<module>../MvnPropApp-Mp2</module>
16.
</modules>
17. </project>
18. Das neue Projekt MvnMultiProj2 und die drei Unterprojekte
19.
20. [\MeinWorkspace]
21.
|- [MvnJaxWs-Mp2]
22.
|
|- [src]
23.
|
|
'- ...
24.
|
'- pom.xml
25.
|- [MvnJettyWebApp-Mp2]
26.
|
|- [src]
27.
|
|
'- ...
28.
|
'- pom.xml
29.
|- [MvnMultiProj2]
30.
|
'- pom.xml
31.
'- [MvnPropApp-Mp2]
32.
|- [src]
33.
|
'- ...
34.
'- pom.xml
sind jetzt so angeordnet:
35. Bauen Sie alle vier Projekte:
cd \MeinWorkspace\MvnMultiProj2
mvn clean install
In der Reactor Summary wird zu allen vier Projekten SUCCESS gemeldet.
36. Entfernen Sie im MvnJaxWs-Mp2-Unterprojekt in der pom.xml den
"<build>...</build>"-Block und rufen Sie im MvnMultiProj2-Verzeichnis erneut
"mvn clean install" auf:
Ab Maven 3.0 erhalten Sie keine Fehlermeldung, aber wenn Sie Maven 2.2.1 verwenden
würden, würden Sie erwartungsgemäß erhalten:
37.
[ERROR] BUILD FAILURE
Compilation failure
...\MvnJaxWs-Mp2\...\HalloWeltImpl.java: annotations are not supported
in -source 1.3
Entfernen Sie im MvnPropApp-Mp2-Unterprojekt in der pom.xml die beiden Zeilen
"<version>4.12</version>" und "<scope>test</scope>" und rufen Sie im
MvnMultiProj2-Verzeichnis erneut "mvn clean install" auf:
Diesmal erhalten Sie wie erwartet die Fehlermeldung:
38.
[ERROR] The project de.meinefirma.meinprojekt:MvnPropApp:1.0-SNAPSHOT
has 1 error
[ERROR] 'dependencies.dependency.version' for junit:junit:jar is
missing.
Ersetzen Sie im MvnMultiProj2-Verzeichnis die pom.xml durch:
39.
40. <project>
41.
<modelVersion>4.0.0</modelVersion>
42.
<groupId>de.meinefirma.meinprojekt</groupId>
43.
<artifactId>MvnMultiProj2</artifactId>
44.
<version>1.0</version>
45.
<packaging>pom</packaging>
46.
<name>MvnMultiProj2</name>
47.
<modules>
48.
<module>../MvnJaxWs-Mp2</module>
49.
<module>../MvnJettyWebApp-Mp2</module>
50.
<module>../MvnPropApp-Mp2</module>
51.
</modules>
52.
<build>
53.
<pluginManagement>
54.
<plugins>
55.
<plugin>
56.
<groupId>org.apache.maven.plugins</groupId>
57.
<artifactId>maven-compiler-plugin</artifactId>
58.
<version>3.6.1</version>
59.
<configuration>
60.
<source>1.7</source>
61.
<target>1.7</target>
62.
</configuration>
63.
</plugin>
64.
</plugins>
65.
</pluginManagement>
66.
</build>
67.
<dependencyManagement>
68.
<dependencies>
69.
<dependency>
70.
<groupId>junit</groupId>
71.
<artifactId>junit</artifactId>
72.
<version>4.12</version>
73.
<scope>test</scope>
74.
</dependency>
75.
</dependencies>
76.
</dependencyManagement>
77. </project>
78. Fügen Sie im MvnJaxWs-Mp2-Unterprojekt in der pom.xml folgendenmaßen einen
"<parent>...</parent>"-Block hinzu (bei der Gelegenheit können Sie auch den
<dependency>...jaxws-api...-Abschnitt entfernen):
79.
80. <project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
81.
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
82.
<modelVersion>4.0.0</modelVersion>
83.
<parent>
84.
<groupId>de.meinefirma.meinprojekt</groupId>
85.
<artifactId>MvnMultiProj2</artifactId>
86.
<version>1.0</version>
87.
<relativePath>../MvnMultiProj2/pom.xml</relativePath>
88.
</parent>
89.
<groupId>de.meinefirma.meinprojekt</groupId>
90.
<artifactId>MvnJaxWs</artifactId>
91.
<version>1.0-SNAPSHOT</version>
92.
<packaging>jar</packaging>
93.
<name>MvnJaxWs</name>
94. </project>
95. Fügen Sie im MvnPropApp-Mp2-Unterprojekt ebenso in der pom.xml zwischen
<modelVersion> und <groupId> den "<parent>...</parent>"-Block hinzu:
96.
97.
<parent>
98.
<groupId>de.meinefirma.meinprojekt</groupId>
99.
<artifactId>MvnMultiProj2</artifactId>
100.
<version>1.0</version>
101.
<relativePath>../MvnMultiProj2/pom.xml</relativePath>
102.
</parent>
103.
Bauen Sie wieder alle vier Projekte:
cd \MeinWorkspace\MvnMultiProj2
mvn clean install
In der Reactor Summary wird wieder zu allen vier Projekten SUCCESS gemeldet.
Multimodulprojekt mit Corporate POM
Im letzten Beispiel wurde gezeigt, wie Sie bei vielen Unterprojekten gemeinsame Einstellungen
(z.B. Java-Versionen und Versionen von Libs) zentral in einem Parent-Multimodulprojekt
verwalten.
Falls Sie mehrere Projekte (oder Multimodulprojekte) haben, sollten Sie noch einen Schritt
weiter gehen, und auch die gemeinsamen Einstellungen der Projekte zentral verwalten, nämlich
in einer "Corporate POM" (oder "Firmen-POM", "Projekt-POM", "Master POM", "Top POM",
...).
Bitte verwechseln Sie die hier gemeinte selbsterstellte übergeordnete Corporate POM nicht mit
der von Maven immer als Grundlage verwendeten Super POM pom-4.0.0.xml (die Sie in der
Maven-Installation in der lib/maven-model-builder-3.5.0.jar finden).
1. Voraussetzung ist, dass Sie das Beispiel Multimodulprojekt mit Plugin-Management und
Dependency-Management (MvnMultiProj2) erfolgreich durchgeführt haben.
(Alternativ können Sie auch das Multimodulprojekt downloaden.)
2. Kopieren Sie die drei Unterprojekte in neue Verzeichnisse, erstellen Sie ein neues
Multimodulprojekt in dem neuen Verzeichnis MvnMultiProj3 und legen Sie das neue
Corporate-POM-Projekt MvnCorpPom an:
cd \MeinWorkspace
xcopy MvnJaxWs-Mp2 MvnJaxWs-Mp3\ /S
xcopy MvnJettyWebApp-Mp2 MvnJettyWebApp-Mp3\ /S
xcopy MvnPropApp-Mp2 MvnPropApp-Mp3\ /S
md MvnMultiProj3
md MvnCorpPom
3. Ersetzen Sie sowohl in der MvnJaxWs-Mp3\pom.xml als auch in der MvnPropAppMp3\pom.xml jeweils an zwei Stellen:
MvnMultiProj2 durch MvnMultiProj3.
4. Erzeugen Sie im MvnMultiProj3-Verzeichnis folgende pom.xml:
5.
6. <project>
7.
<modelVersion>4.0.0</modelVersion>
8.
<parent>
9.
<groupId>de.meinefirma.meinprojekt</groupId>
10.
<artifactId>MvnCorpPom</artifactId>
11.
<version>1.0</version>
12.
<relativePath>../MvnCorpPom/pom.xml</relativePath>
13.
</parent>
14.
<groupId>de.meinefirma.meinprojekt</groupId>
15.
<artifactId>MvnMultiProj3</artifactId>
16.
<version>1.0</version>
17.
<packaging>pom</packaging>
18.
<name>MvnMultiProj3</name>
19.
<modules>
20.
<module>../MvnJaxWs-Mp3</module>
21.
<module>../MvnJettyWebApp-Mp3</module>
22.
<module>../MvnPropApp-Mp3</module>
23.
</modules>
24. </project>
25. Erzeugen Sie im MvnCorpPom-Verzeichnis folgende pom.xml:
26.
27. <project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
28.
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
29.
<modelVersion>4.0.0</modelVersion>
30.
<groupId>de.meinefirma.meinprojekt</groupId>
31.
<artifactId>MvnCorpPom</artifactId>
32.
<version>1.0</version>
33.
<packaging>pom</packaging>
34.
<name>MvnCorpPom</name>
35.
<properties>
36.
<project.build.sourceEncoding>ISO-88591</project.build.sourceEncoding>
37.
<project.reporting.outputEncoding>UTF8</project.reporting.outputEncoding>
38.
</properties>
39.
<build>
40.
<pluginManagement>
41.
<plugins>
42.
<plugin>
43.
<groupId>org.apache.maven.plugins</groupId>
44.
<artifactId>maven-compiler-plugin</artifactId>
45.
<version>3.6.1</version>
46.
<configuration>
47.
<source>1.7</source>
48.
<target>1.7</target>
49.
</configuration>
50.
</plugin>
51.
</plugins>
52.
</pluginManagement>
53.
</build>
54.
<dependencyManagement>
55.
<dependencies>
56.
<dependency>
57.
<groupId>junit</groupId>
58.
<artifactId>junit</artifactId>
59.
<version>4.12</version>
60.
<scope>test</scope>
61.
</dependency>
62.
</dependencies>
63.
</dependencyManagement>
64. </project>
65. Bauen Sie die Corporate POM und installieren Sie sie im lokalen Maven-Repository:
cd \MeinWorkspace\MvnCorpPom
mvn install
66. Wechseln Sie in das Multimodulprojekt und bauen Sie es inklusive aller Unterprojekte:
cd \MeinWorkspace\MvnMultiProj3
mvn clean install
In der Reactor Summary wird wieder zu allen vier Projekten SUCCESS gemeldet.
67. Testen Sie das Webprojekt:
cd \MeinWorkspace\MvnJettyWebApp-Mp3
mvn jetty:run
Rufen Sie die Webseite auf:
start http://localhost:8080/MvnJettyWebApp
68. In diesem Beispiel erben die Untermodule vom übergeordneten Multimodulprojekt
MvnMultiProj3, welches wiederum von der Corporate POM aus MvnCorpPom erbt.
Alternativ ist es oft sinnvoll, wenn die Untermodule stattdessen direkt die Corporate
POM aus MvnCorpPom einbinden. Dann ist es einfacher, mehrere verschiedene
Multimodulprojekte für verschiedene Zwecke einzurichten, wie etwa für Integrationstests
auf dem Entwicklungsrechner, Systemtests in einer Testumgebung, Continuous
Integration, Release-Auslieferungen etc. (falls es zu aufwändig ist, dies über MavenProfile oder TestNG-Gruppen zu steuern).
69. Die Corporate POM können Sie erweitern und in beliebige weitere andere Projekte
einbinden. In Firmen mit mehreren Projekten ist üblich, eine zentrale Corporate POM für
firmenweite Voreinstellungen vorzusehen und zusätzlich pro Projekt eine Projekt-MasterPOM für Projektspezifisches zu erstellen. Die Projektmodule binden als Parent die
Projekt-Master-POM ein, und diese erbt von der Corporate POM.
70. Das oben gezeigte Beispiel verwendet das so genannte "Flat Project Layout": Die MavenModule sind in der Verzeichnisstruktur parallel angeordnet. Alternativ könnten Sie die
Maven-Projekthierarchie auch in einer hierarchischen Verzeichnisstruktur abbilden, also
die Untermodule in Unterverzeichnisse zum Parent-Multimodulprojekt speichern. Zu den
Vor- und Nachteilen sehen Sie sich den Vergleich des "Flat Project Layout" zum
"Hierarchical Project Layout" an.
71. Falls die groupId und/oder die version der Untermodule identisch zum übergeordneten
Parent-Modul (z.B. Projekt-Master-POM) sein soll, sollten Sie die Angaben nicht
redundant wiederholen, sondern über <groupId>${project.groupId}</groupId> bzw.
<version>${project.version}</version> einbinden. Im project-Kopf der
abhängigen Module lassen Sie die Angaben einfach weg, dann werden sie automatisch
geerbt (andernfalls würde Maven 3 melden: "[WARNING] 'groupId' contains an
expression but should be a constant").
72. Falls die version von Untermodulen nicht identisch zum übergeordneten Parent-Modul
sein soll und es Abhängigkeiten zwischen den Untermodulen gibt, dann sollten Sie
vermeiden, die Versionsnummer des von anderen Untermodulen verwendeten
Untermoduls redundant direkt in den Dependencies einzutragen. Verwalten Sie auch die
Versionsnummern der Untermodule in der Parent-POM (z.B. Projekt-Master-POM).
Allerdings müssen Sie sicherstellen, dass alle Module stets die aktuellste Parent-POM
verwenden und nicht irgendeine zufällig in einem (z.B. lokalen) Repository vorhandene.
Besonders in der Startphase eines Projekts, in der es viele Änderungen gibt, kann es
praktischer sein, die Projekt-Master-POM nicht im Maven-Repository zur Verfügung zu
stellen (also für die Projekt-Master-POM weder mvn install noch mvn deploy
auszuführen). Dann muss zusätzlich zum in Bearbeitung befindlichem Modul stets auch
das Projekt-Master-POM-Modul aus dem Versionskontrollsystem (Git, Mercurial,
Subversion, CVS, ...) ausgecheckt sein und beide Module können regelmäßig
abgeglichen und aktualisiert werden. Damit dies funktioniert, muss beim Flat Project
Layout der relativePath korrekt gesetzt sein (wie obiges Beispiel zeigt).
73. Bitte beachten Sie, dass es leider zwei kaum zu vermeidende Probleme gibt:
o Bei allen Untermodulen muss die Versionsnummer des Parent-Moduls redundant
eingetragen werden. Wenn sich die Versionsnummer des Parent-Moduls ändert,
müssen alle abhängigen Module angepasst werden.
o Strenggenommen gibt es häufig zyklische Abhängigkeiten zwischen den POMs:
Untermodule müssen immer ihr Parent-Modul kennen (egal ob Projekt-MasterPOM oder Multimodulprojekt-POM). Umgekehrt müssen Multimodulprojekte
immer ihre Untermodule kennen, und, falls Versionsnummern von Untermodulen
in der Projekt-Master-POM verwaltet werden, hat auch diese Kenntnisse über die
Untermodule.
Während programmiertechnische Abhängigkeiten (z.B. Java-Abhängigkeiten)
zwischen Modulen niemals zyklisch sein sollten, stellen diese genannten POMAbhängigkeitszyklen meistens kein Problem dar.
74. Planen Sie genau, wie ein Release für eine Auslieferung erstellt werden soll. Oft werden
für jedes einzelne Modul eines Projekts folgende Schritte benötigt:
1. Abgleich mit dem Versionskontrollsystem (Git, Mercurial, Subversion, CVS, ...).
2. Prüfen, ob noch SNAPSHOT-Abhängigkeiten existieren.
3. Die Versionsbezeichnung in der POM wie für das Release gewünscht setzen
(ohne SNAPSHOT) und die POM ins Versionskontrollsystem einchecken.
4. In allen abhängigen Modulen die neue Parent-Versionsbezeichnung eintragen und
die Module ins Versionskontrollsystem einchecken.
5. Wenn in allen Modulen alle SNAPSHOT-Versionen ersetzt sind, setzen eines
Release-Tags im Versionskontrollsystem für alle Module.
6. Build und Test sowie install ins lokale Maven-Repository.
7. Eventuell Deployment ins Remote-Maven-Repository (siehe hierzu auch
Distribution-Deployment).
8. Dokumentationen, ER-Diagramme etc. aktualisieren.
9. Die Ergebnis-Artefakte in ein Auslieferverzeichnis kopieren. Dazu gehören nicht
nur JAR-, WAR- und EAR-Dateien, sondern auch Datenbankänderungsskripte,
Konfigurations-Properties und Übergabeprotokolle.
10. Die Versionsbezeichnung in den POMs auf die nächste Entwickler-SNAPSHOTVersion setzen und POMs einchecken.
Bei diesen Schritten kann das Maven Release Plugin Unterstützung leisten.
Voraussetzung ist eine korrekte SCM-Konfiguration. Bitte beachten Sie, dass hier wie
auch beim Maven-SCM-Plugin mit "SCM" nicht das übergreifende "Software
Configuration Management" gemeint ist, sondern lediglich "Source Code Management",
also das Versionskontrollsystem (VCS).
Falls bei komplizierteren Projekten das Maven Release Plugin nicht ausreicht: Sehen Sie
sich das Versions Maven Plugin an.
Sehen Sie sich auch folgende Erläuterungen an: Versionierung und Release-Build mit
dem "Maven Release Plugin", dem "Versions Maven Plugin" und dem "Maven SCM
Plugin".
Site Report um Project Reports zur Sourcecodeanalyse erweitern
Vorbemerkungen zu Site Project Reports
Es gibt viele lohnenswerte Maven-Plugins, die automatisch Informationen zu Ihrem Projekt
aufbereiten. Im Folgenden wird die Einbindung einiger solcher Maven-Plugins beispielhaft
gezeigt.
Bei den bisherigen Mini-Demos machen Project Reports kaum Sinn, aber vielleicht haben Sie
ein größeres Projekt mit mehr Sourcedateien.
Es werden diverse Metriken zur Sourcecodequalität ermittelt, zum Beispiel zu: Testabdeckung,
potenzielle Fehler, Abhängigkeitsprobleme und strukturelle Probleme.
Bitte beachten Sie:




Einige Plugins entfalten ihre volle Stärke erst, wenn sie genauer konfiguriert werden.
Nicht alle Metriken sind bei allen Projekten gleichermaßen sinnvoll oder vergleichbar.
Sie können keinesfalls anhand solcher Metriken Softwareentwickler beurteilen, weil Sie
das Gegenteil bewirken würden: Viele Metriken lassen sich leicht durch Tricks
"verbessern", was allerdings zu Verschlechterungen des Sourcecodes führt.
Trotz dieser Einschränkungen sind viele der Ergebnisse für den Softwareentwickler eine
wichtige Hilfe zur Sourcecodeanalyse und Qualitätsverbesserung.
Sie können beispielsweise folgende Ergebnisse erhalten:
Project Information:
Auflistung der Dependencies und "Dependency Tree"
maven-project-inforeports-plugin
Checkstyle
Coding-Standards
maven-checkstyle-plugin
Cobertura Test
Coverage
Testabdeckung ermitteln (Cobertura nur bis Java 7)
cobertura-maven-plugin
CPD Report
Code-Dubletten finden (Copy/Paste Detector)
maven-pmd-plugin
Dependency
Analysis
Auflistung der Dependencies und unused/undeclaredmaven-dependency-plugin
Überprüfung
FindBugs Report
Potenzielle Fehler im Sourcecode per BytecodeAnalyse finden
Dependencies
Project Reports:
findbugs-maven-plugin
JavaDocs
Javadoc-Dokumentation
maven-javadoc-plugin
PMD Report
Potenzielle Probleme im Sourcecode finden
maven-pmd-plugin
Maven JXR
HTML-basierende Verlinkung des Java-Sourcecodes
maven-jxr-plugin
Surefire Report
Testergebnisse
maven-surefire-reportplugin
Tag List
Bestimmte im Sourcecode enthaltene Tags auflisten
(z.B. TODO, FIXME)
taglist-maven-plugin
Checkstyle, PMD und FindBugs scheinen sich auf den ersten Blick zu ähneln. Alle drei suchen
nach bekannten Fehlermustern (Bug Patterns). Da sie aber verschieden funktionieren, finden sie
unterschiedliche Fehler. Zum Beispiel analysieren Checkstyle und PMD den Sourcecode,
während FindBugs den erzeugten Bytecode untersucht und so NullPointer, nicht geschlossene
Datenströme und fehlerhafte Synchronisierungen aufspürt.
Tragen Sie Ihr korrektes sourceEncoding ein, also das Encoding, mit dem Sie Ihre
Sourcedateien speichern (unter Linux meistens UTF-8, unter Windows meistens Cp1252, unter
Eclipse konfigurierbar), sowie Ihr gewünschtes reporting.outputEncoding (z.B. UTF-8).
Binden Sie den folgenden <properties>-Block in die pom.xml ein:
<properties>
<project.build.sourceEncoding>ISO-8859-1</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF8</project.reporting.outputEncoding>
</properties>
Site Project Reports mit Maven 3.5
1. Zu Site Reports gibt es seit Maven 3.0 erhebliche Änderungen, siehe hierzu: Maven
Release Notes, Maven 3.x Compatibility Notes und Using maven-site-plugin with Maven
3.
2. Binden Sie die folgenden <properties>- und <reporting>-Blöcke in einem beliebigen
Projekt in die pom.xml ein:
3.
4. ...
5.
<properties>
6.
<project.build.sourceEncoding>ISO-88591</project.build.sourceEncoding>
7.
<project.reporting.outputEncoding>UTF8</project.reporting.outputEncoding>
8.
</properties>
9. ...
10.
<reporting>
11.
<outputDirectory>${project.build.directory}/site</outputDirectory>
12.
<plugins>
13.
<plugin>
14.
<groupId>org.apache.maven.plugins</groupId>
15.
<artifactId>maven-site-plugin</artifactId>
16.
<version>3.5.1</version>
17.
</plugin>
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-project-info-reports-plugin</artifactId>
<version>2.9</version>
</plugin>
<!-- Falls Versionskontrollsystem konfiguriert ist:
<plugin>
<artifactId>maven-changelog-plugin</artifactId>
</plugin>
-->
<plugin>
<artifactId>maven-checkstyle-plugin</artifactId>
<version>2.17</version>
<configuration>
<configLocation>google_checks.xml</configLocation>
<includeTestSourceDirectory>true</includeTestSourceDirectory>
<excludes>**/generated/*.java</excludes>
</configuration>
</plugin>
<plugin>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.10</version>
<configuration>
<repositoryUrl>--</repositoryUrl>
<artifactItems />
</configuration>
</plugin>
<plugin>
<artifactId>maven-javadoc-plugin</artifactId>
<version>2.10.4</version>
</plugin>
<plugin>
<artifactId>maven-jxr-plugin</artifactId>
<version>2.5</version>
</plugin>
<plugin>
<artifactId>maven-pmd-plugin</artifactId>
<version>3.6</version>
<configuration>
<targetJdk>1.7</targetJdk>
<format>xml</format>
<linkXref>true</linkXref>
<sourceEncoding>ISO-8859-1</sourceEncoding>
<minimumTokens>100</minimumTokens>
<excludes>
<exclude>**/generated/*.java</exclude>
</excludes>
</configuration>
</plugin>
<plugin>
<artifactId>maven-surefire-report-plugin</artifactId>
<version>2.20</version>
<configuration>
<!-- Bei grossen Projekten auf false setzen: -->
<showSuccess>true</showSuccess>
</configuration>
74.
75.
76.
</plugin>
<plugin>
<!-- Cobertura 2.1.1 und das cobertura-maven-plugin 2.7
funktionieren nur bis Java 7: -->
77.
<groupId>org.codehaus.mojo</groupId>
78.
<artifactId>cobertura-maven-plugin</artifactId>
79.
<version>2.7</version>
80.
</plugin>
81.
<plugin>
82.
<groupId>org.codehaus.mojo</groupId>
83.
<artifactId>findbugs-maven-plugin</artifactId>
84.
<version>3.0.3</version>
85.
</plugin>
86.
<plugin>
87.
<groupId>org.codehaus.mojo</groupId>
88.
<artifactId>taglist-maven-plugin</artifactId>
89.
<version>2.4</version>
90.
<configuration>
91.
<tags>
92.
<tag>fixme</tag>
93.
<tag>FixMe</tag>
94.
<tag>FIXME</tag>
95.
<tag>@todo</tag>
96.
<tag>todo</tag>
97.
<tag>TODO</tag>
98.
<tag>xxx</tag>
99.
<tag>@deprecated</tag>
100.
</tags>
101.
</configuration>
102.
</plugin>
103.
<!-- Falls SonarQube verwendet wird:
104.
<plugin>
105.
<groupId>org.codehaus.sonar-plugins</groupId>
106.
<artifactId>maven-report</artifactId>
107.
<version>0.1</version>
108.
</plugin>
109.
-->
110.
</plugins>
111.
</reporting>
112. ...
113.
Rufen Sie Folgendes auf und sehen Sie sich die Ergebnisseiten an:
mvn site
start target\site\index.html
start target\site\project-info.html
114.
Falls Sie beispielsweise so dass obige JAXB-Beispiel MvnJaxbApp untersuchen,
erhalten Sie:
Sourcecodeanalyse mit SonarQube (früher Sonar)
Funktion von SonarQube
SonarQube erleichtert Sourcecodeanalyse. SonarQube liefert ähnliche Ergebnisse, wie die oben
unter "Site Report um Project Reports zur Sourcecodeanalyse erweitern" gezeigten Plug-ins. Die
wichtigsten Unterschiede sind:


Vorteile: Die Ergebnisse sind übersichtlicher aufbereitet, komfortabler auszuwerten und
inklusive einer Historisierung. Außerdem braucht das zu untersuchende Projekt nicht
modifiziert zu werden (anders als beim obigen Site Report, der nur mit zusätzlichen
Einträgen in der POM vollständige Ergebnisse liefert).
Nachteil: Es muss ein zusätzlicher SonarQube-Server installiert und gestartet werden.
Idealerweise wird SonarQube in ein "Continuous Integration"-System eingebunden (z.B.
Jenkins, Hudson oder TeamCity), siehe hierzu auch: Continuous Integration mit Jenkins /
Hudson und Maven 3.
SonarQube ermittelt diverse Metriken zur Sourcecodequalität, zum Beispiel zu: typische
Programmierfehler, potenzielle Fehler, Testabdeckung, Abhängigkeitsprobleme und strukturelle
Probleme.
Auch hier gilt wieder:




Einige Plugins entfalten ihre volle Stärke erst, wenn sie genauer konfiguriert werden.
Nicht alle Metriken sind bei allen Projekten gleichermaßen sinnvoll oder vergleichbar.
Sie können keinesfalls anhand solcher Metriken Software oder Softwareentwickler
beurteilen, weil Sie das Gegenteil bewirken würden: Viele Metriken lassen sich leicht
durch Tricks "verbessern", was allerdings zu Verschlechterungen des Sourcecodes führt.
SonarQube ist für die Kontrolle durch das Management ungeeignet, aber eine wichtige
Hilfe für den Softwareentwickler zur Sourcecodeanalyse und Qualitätsverbesserung.
Dokus
Siehe: Documentation, Requirements, Installing the Server, Running SonarQube as a Service on
Windows, ... on Linux, Analyzing with SonarQube Scanner for Maven
Basis-Installation und Vorbereitung
1. Downloaden Sie SonarQube (z.B. sonarqube-6.3.zip) von
http://www.sonarqube.org/downloads. Entzippen Sie SonarQube, z.B. unter Windows
nach \Tools oder unter Linux nach /opt.
2. Neuere SonarQube-Versionen setzen mindestens Java 8 voraus. Überprüfen Sie dies mit:
java -version
3. Prüfen Sie, ob die SonarQube-Default-Portnummer 9000 noch frei ist:
netstat -an | find ":9000"
Falls der Port nicht frei ist: Tragen Sie in \Tools\SonarQube\conf\sonar.properties
bei "sonar.web.port" eine andere Portnummer ein (und entfernen Sie das vorangestellte
Auskommentierungszeichen '#').
4. Prüfen Sie, ob die für die SonarQube-Datenbank benötigte Portnummer noch frei ist.
Falls Sie die defaultmäßige integrierte Datenbank verwenden wollen: Je nach
SonarQube-Version ist dies eine Derby Database (mit Port 1527) oder eine H2 Database
(mit Port 9092). Prüfen Sie zum Beispiel so:
netstat -a
netstat -an | find ":1527"
netstat -an | find ":9092"
5. Falls Sie eine Oracle-Datenbank verwenden wollen, überprüfen Sie Folgendes:
Die DB muss für das Character-Encodung UTF-8 konfiguriert sein, optimalerweise so:
NLS_CHARACTERSET = AL32UTF8. Überprüfen Sie dies mit dem SQL-Kommando
"Select * from nls_database_parameters;".
Zumindest bei früheren SonarQube-Versionen durfte die Sortierung nicht für GERMAN
konfiguriert sein, sondern so: NLS_SORT = BINARY. Überprüfen Sie dies mit demselben
SQL-Kommando.
Laut Requirements soll ein Oracle-11.2.x-Treiber verwendet werden, und kein Oracle12.x-Treiber ("Oracle 12.x drivers are not supported").
Downloaden und kopieren Sie den Oracle-11.2.x-JDBC-Treiber (z.B. ojdbc6.jar in
Version 11.2.0.4.0) nach: \Tools\SonarQube\extensions\jdbc-driver\oracle.
Tragen Sie in \Tools\SonarQube\conf\sonar.properties die DB-ConnectionParameter ein:
sonar.jdbc.username=...
sonar.jdbc.password=...
sonar.jdbc.url=jdbc:oracle:thin:@localhost:1521:XE
sonar.jdbc.driverClassName=oracle.jdbc.driver.OracleDriver
sonar.jdbc.validationQuery=select 1 from dual
SonarQube-Server starten
1. Rufen Sie auf (passen Sie den Pfad an):
Unter Windows:
start "SonarQube-Server" \Tools\SonarQube\bin\windows-x8664\StartSonar.bat
bzw. unter Linux:
/opt/sonarqube/bin/linux-x86-64/sonar.sh start
Warten Sie, bis der SonarQube-Server fertig gestartet ist, was schon mal mehrere
Minuten dauern kann.
Sehen Sie sich die Logdateien an:
type \Tools\SonarQube\logs\*.log
bzw.
cat /opt/sonarqube/logs/*.log
Rufen Sie auf:
start http://localhost:9000
2. Falls Sie eine Fehlermeldung erhalten ähnlich zu:
Encountered an error running main: java.io.IOException: Unable to
create directory \Tools\SonarQube\temp
Dann rufen Sie StartSonar.bat mit Admin-Rechten auf (über rechter Maustaste).
3. Um SonarQube als automatisch beim PC-Start startenden Service einzurichten, sehen Sie
sich an: Running SonarQube as a Service on Windows, Running SonarQube as a Service
on Linux, zusätzliche Hinweise.
Besondere Vorgaben
1. Dies ist normalerweise unnötig, aber falls Sie besondere Einstellungen benötigen, können
diese folgendermaßen in dem zu analysierenden Projekt in die pom.xml (bzw. falls
vorhanden besser in eine Master-POM) eingetragen werden:
2.
3.
4.
5.
...
<properties>
<!-- Wird nur benoetigt, wenn eine abweichende SonarQube-URL (z.B.
Remote) eingetragen werden muss: -->
6.
<sonar.host.url>http://localhost:9000</sonar.host.url>
7.
...
8.
</properties>
9.
<build>
10.
<plugins>
11.
<!-- Wird nur benoetigt, falls eine bestimmte Versionsnummer
vorgegeben werden muss: -->
12.
<plugin>
13.
<groupId>org.sonarsource.scanner.maven</groupId>
14.
<artifactId>sonar-maven-plugin</artifactId>
15.
<version>3.2</version>
16.
</plugin>
17.
...
18.
</plugins>
19.
</build>
20.
...
Aufruf und Analyse für ein einzelnes Maven-Projekt
1. Rufen Sie auf (passen Sie den Pfad an):
cd \MeinWorkspace\<MeinProjekt>
mvn clean install sonar:sonar
Warten Sie, bis der Analyseprozess fertig durchgelaufen ist, was schon mal mehrere
Minuten dauern kann. Rufen Sie anschließend auf:
start http://localhost:9000
2. Falls beim sonar:sonar-Aufruf falsche Versionen gestartet werden, oder die
SonarQube-Server-URL hinzugefügt werden soll, können Sie statt des abgekürzten
Kommandos folgendes vollständige Kommando verwenden (vorher anpassen):
mvn clean install org.sonarsource.scanner.maven:sonar-mavenplugin:3.2:sonar -Dsonar.host.url=http://localhost:9000
3. Falls Sie die Java Code Coverage der JUnit-Tests (= Testabdeckung) erfassen wollen und
SonarQube keine Ergebnisse meldet, verwenden Sie folgendermaßen das JaCoCo-Plugin:
cd \MeinWorkspace\<MeinProjekt>
mvn clean org.jacoco:jacoco-maven-plugin:prepare-agent install Dmaven.test.failure.ignore=true
mvn sonar:sonar
start http://localhost:9000
Falls Sie das maven-surefire-plugin oder das maven-failsafe-plugin verwenden,
beachten Sie die Hinweise zum JaCoCo-Maven Plug-in und zum prepare-agent-Goal.
Falls Sie auf Probleme stoßen, sehen Sie sich an: Probleme mit dem JaCoCo-MavenPlugin.
Aufruf und Analyse für viele Maven-Projekte
1. Falls Sie mehrere Maven-Projekte in parallelen Unterverzeichnissen zu einem
gemeinsamen Projekteverzeichnis haben, können Sie mit einem einzigen Aufruf alle
Maven-Projekte
o mit SonarQube analysieren und
o zusätzlich Site-Reports erstellen.
Für die SonarQube-Analyse brauchen Sie in den einzelnen Projekten nichts
vorzubereiten. Die in der Windows-Batchdatei erstellten Site-Reports dagegen machen
erst dann wirklich Sinn, wenn Sie geeignete Vorgaben in den jeweiligen POMs (bzw.
falls vorhanden in einer gemeinsamen Master-POM) erstellt haben, etwa so wie oben
unter Site Project Reports gezeigt.
2. Windows:
Wechseln Sie in Ihr Projekte-Verzeichnis und erzeugen Sie ein neues Unterverzeichnis
für die Windows-Batchdatei:
cd \MeinWorkspace
md SonarQubeUndSiteReport
cd SonarQubeUndSiteReport
Erzeugen Sie im SonarQubeUndSiteReport-Verzeichnis die Batchdatei: runSonarQubeUndSiteReport-all.bat
@title run-SonarQubeUndSiteReport-all.bat
cls
@echo.
@echo -----------------------------------------------------------------------@echo run-SonarQubeUndSiteReport-all.bat
@echo -----------------------------------------------------------------------@echo.
set _SONAR_START_BAT=\Tools\SonarQube\bin\windows-x86-64\StartSonar.bat
set _SITE_REPORTS_DIR=\MeineSiteReports
set _IGNORED_DIRS=MvnAppSrvFailsaveIntTest MvnAppSrvIntTest
MvnMultiProj1 MvnMultiProj2 MvnMultiProj3 MvnSign
SonarQubeUndSiteReport
@if not exist "%_SONAR_START_BAT%" goto _Fehler
@if not exist "%_SITE_REPORTS_DIR%" md "%_SITE_REPORTS_DIR%"
@if not exist "%_SITE_REPORTS_DIR%" goto _Fehler
netstat -an | find "0.0.0.0:9000"
@if not errorlevel 1 goto _Sonar_Server_fertig
start "SonarQube" "%_SONAR_START_BAT%"
:_Warte_Sonar_Server
@echo Warten auf den SonarQube-Server ...
@ping -n 5 127.0.0.1>nul
@netstat -an | find "0.0.0.0:9000"
@if errorlevel 1 goto _Warte_Sonar_Server
:_Sonar_Server_fertig
start http://localhost:9000
pushd .
cd ..
@echo off
FOR /F %%i in ('dir /B /A:D') DO (
echo.
call :checkignored %%i
if not errorlevel 1 (
if exist %%i\pom.xml (
echo.
echo -----------------------------------------------------------------------echo Projektverzeichnis: %%i
echo -----------------------------------------------------------------------echo on
cd %%i
echo.
echo ---- SonarQube fuer %%i:
echo.
call mvn clean org.jacoco:jacoco-maven-plugin:prepare-agent
install -Dmaven.test.failure.ignore=true
call mvn sonar:sonar
if errorlevel 1 goto _Fehler
@echo on
echo.
echo ---- Site-Reports fuer %%i:
echo.
if exist "%_SITE_REPORTS_DIR%\%%i\site" rd /S /Q
"%_SITE_REPORTS_DIR%\%%i\site"
call mvn clean site
if errorlevel 1 goto _Fehler
echo on
xcopy target\site "%_SITE_REPORTS_DIR%\%%i\site\" /S
if exist %_SITE_REPORTS_DIR%\%%i\site\project-info.html (
start %_SITE_REPORTS_DIR%\%%i\site\project-info.html
)
@echo.
cd ..
echo off
)
)
)
@echo
start http://localhost:9000
@goto _fertig
:checkignored
FOR /D %%j in (%_IGNORED_DIRS%) DO IF "%1"=="%%j" EXIT /B 1
exit /B 0
:_Fehler
@echo.
@echo -----------------------------------------------------------------------@echo !!! Fehler !!!
@echo -----------------------------------------------------------------------@echo
pause
:_fertig
@echo on
@echo.
popd
@echo.
3. Linux:
Wechseln Sie in Ihr Projekte-Verzeichnis und erzeugen Sie ein neues Unterverzeichnis
für das Linux-Shell-Skript:
cd ~/MeinWorkspace
mkdir SonarQube
cd SonarQube
Erzeugen Sie im SonarQube-Verzeichnis das Linux-Shell-Skript: run-SonarQubeall.sh
#!/bin/bash
echo
echo -----------------------------------------------------------------------echo run-SonarQube-all.sh
echo -----------------------------------------------------------------------echo
_IGNORED_DIRS=" MvnAppSrvFailsaveIntTest MvnAppSrvIntTest MvnMultiProj1
MvnMultiProj2 MvnMultiProj3 MvnSign SonarUndSiteReport "
if ! netstat -ant | grep -q ':9000'
then
/opt/sonarqube/bin/linux-x86-64/sonar.sh start &
fi
while ! netstat -ant | grep -q ':9000'
do echo Warten auf den SonarQube-Server ... ; sleep 5
done
echo
cd ..
echo "Aktuelles Verzeichnis: "$(pwd)
echo
ls -l
echo
for verzeichnis in *
do
echo -e "\n------------------------------------------------------"
if [[ $_IGNORED_DIRS == *${verzeichnis}* ]]
then
echo "---- "${verzeichnis}" ist zu ignorieren ----"
elif [ ! -d "${verzeichnis}" ]
then
echo "---- "${verzeichnis}" ist kein Verzeichnis ----"
elif [ ! -f "${verzeichnis}/pom.xml" ]
then
echo "---- "${verzeichnis}" enthaelt keine pom.xml ----"
else
echo "---- "${verzeichnis}":"
cd "${verzeichnis}"
mvn clean org.jacoco:jacoco-maven-plugin:prepare-agent install Dmaven.test.failure.ignore=true
mvn sonar:sonar || exit 1
cd ..
fi
done
echo -e "\n------------------------------------------------------\n"
( speaker-test -t sine -f 1000 > /dev/null )& pid=$! ; sleep 0.1s ;
kill -9 $pid
echo -e "\n++++ OK ++++\n"
4. Je nach SonarQube-Version können Sie beispielsweise erhalten:
Konfiguration und SonarQube-Weboberfläche
(nur bis SonarQube 5.6.x und teilweise 6.0, ab 6.1 gibt es massive Änderungen, z.B. gibt es
kein Dashboard mehr)
1. Konfigurieren Sie das Aussehen der Dashboard-Startseite, indem Sie sich über "Log in"
mit "admin/admin" einloggen und "Dashboards" | "Home" | "Configure widgets"
anklicken.
Meistens empfiehlt es sich, das "Measure Filter as List"-Widget auf die gesamte Breite zu
vergrößern, indem Sie rechts oben nicht das zweigeteilte Rechteck, sondern stattdessen
das oberste ungeteilte Rechteck aktivieren.
Außerdem sollten Sie dem "Measure Filter as List"-Widget noch eine Spalte für die
Testabdeckung hinzufügen über:
Im "Measure Filter as List"-Widget klicken auf "Projects" | "Change Columns" | in der
Drop-Down-Liste unter Tests "Coverage" wählen | "Add Column" | die neue Spalte eins
nach links verschieben | "Close" | "Save" | zurück mit "Dashboards".
Insgesamt könnten beispielsweise folgende weiteren Spalten sinnvoll sein:
NAME, VERSION, LOC, SQALE RATING, TECHNICAL DEBT, COVERAGE,
BLOCKER ISSUES, CRITICAL ISSUES, ISSUES, LAST ANALYSIS.
Außerdem kann eine Ansicht "Measure Filter as Treemap" sinnvoll sein, beispielsweise
mit "Size: Lines of Code" und "Color: Technical Dept Ratio".
2. Sehen Sie sich die Konfiguration an und führen Sie Anpassungen durch:
Loggen Sie sich mit "admin/admin" ein, klicken Sie auf "Settings" | "Quality Profiles",
und wählen Sie das geeignetste Ausgangsprofil, falls vorhanden am besten "Sonar way
with FindBugs". Kopieren Sie das gewählte Ausgangsprofil mit "Copy", vergeben Sie
einen neuen Namen (ohne Sonderzeichen und ohne Leerzeichen) und setzen Sie die
Kopie mit "Set as default" als Standard. Klicken Sie auf den neuen Profilnamen und
klicken Sie auf die "Coding rule"-Namen, um die Regeln zu konfigurieren. Kandidaten
für eine etwas weniger strenge Bewertung könnten sein:
"Avoid commented-out lines of code", "Dodgy - instanceof will always return true", "Dodgy - int division
result cast to double or float", "Dodgy - Redundant nullcheck of value known to be non-null", "Dodgy Write to static field from instance method", "Duplicated blocks", "Execution of the Garbage Collector
should be triggered only by the JVM", "Left curly braces should be located at the end of lines of code",
"Malicious code vulnerability - May expose internal representation by incorporating reference to mutable
object", "Malicious code vulnerability - May expose internal representation by returning reference to
mutable object", "Methods should not be too complex", "Performance - Inefficient use of keySet iterator
instead of entrySet iterator", "Right curly braces should be located at the beginning of lines of code",
"Statements should be on separate lines", "Switch cases should end with an unconditional break statement",
"System.exit(...) and Runtime.getRuntime().exit(...) should not be called", "The Object.finalize() method
should never be overridden"
3. Falls es das kombinierte Profil "Sonar way with FindBugs" nicht gibt, aber stattdessen
die zwei Profile "Sonar way" und "FindBugs": Folgendermaßen können Sie Ihre
Projektmodule mit beiden Profilen analysieren:
call mvn clean install sonar:sonar -Dsonar.profile="MeinFindBugsProfil"
-Dsonar.branch=FindBugs
call mvn clean install sonar:sonar -Dsonar.profile="MeinSonarWayProfil"
-Dsonar.branch=SonarWay
4. Falls Sie mehr als 20 Projekte haben und das SonarQube-Dashboard mehr als nur 20
Projekte pro Seite anzeigen soll, erhöhen Sie die Page Size unter "Configure widgets" im
"Measure Filter as List"-Widget: "Edit" | "Page Size".
5. Falls Sie bestimmte Packages oder Klassen aus der Analyse ausklammern wollen, zum
Beispiel automatisch generierte Klassen (etwa per JAXB), dann geben Sie diese über die
Property "sonar.exclusions" an. Dabei können mehrere durch Komma getrennte Pfade
angegeben werden, beispielsweise: "meinpackage1/generated/*.java,
meinpackage2/**/generated/*.java".
6. Um Speicherplatz zu sparen, sollten Sie die Aufbewahrungszeiten konfigurieren über:
"Log in" | "Settings" | "Configuration" | "General Settings" | "Database Cleaner".
7. Unter "Settings" | "Configuration" | "Backup" können Sie die Konfiguration über den
"Backup"-Button als XML-Datei speichern, entweder als Backup oder um sie auf einen
anderen PC zu übertragen. Das Einlesen erfolgt auf derselben Seite über "Restore".
8. Erläuterungen zu den verwendeten Metriken finden Sie unter:
http://docs.sonarqube.org/display/SONAR/Metric+definitions
9. Sehen Sie sich die Doku zu SonarQube an:
http://docs.sonarqube.org/display/SONAR/Documentation
10. Insbesondere in Continuous-Integration-Systemen (z.B. mit Jenkins/Hudson) sollten Sie
den SonarQube-Server beim Booten automatisch als Windows-Dienst starten. Sehen Sie
sich hierzu an:
http://docs.sonarqube.org/display/SONAR/Running+SonarQube+as+a+Service+on+Win
dows.
Beachten Sie, dass einige SonarQube-Batchdateien (z.B. "SonarQube\bin\windowsx86-64\InstallNTService.bat") nur in einem Kommandozeilenfenster mit
Administratorrechten ausgeführt werden können:
"Start" | "Alle Programme" | "Zubehör" | "Eingabeaufforderung" mit rechter Maustaste
und "Als Administrator ausführen".
Beachten Sie, dass Sie beim installierten Windows-Dienst eventuell WindowsAnmeldeinformationen eintragen müssen:
"Start" | "Systemsteuerung" | ["System und Sicherheit"] | "Verwaltung" | "Dienste" |
Doppelklick auf "SonarQube" | "Anmelden".
11. Beachten Sie, dass für systematischen und wiederholten Einsatz empfohlen wird, statt der
defaultmäßigen integrierten Datenbank eine externe Datenbank zu verwenden. Diesen
und viele weitere Hinweise finden Sie unter:
http://docs.sonarqube.org/display/SONAR/Setup+and+Upgrade
12. Falls Sie JSP oder JSF verwenden, empfiehlt es sich, zusätzlich das SonarQube Web
Plugin zu installieren:
Downloaden Sie die Plugin-Jar-Datei (z.B. sonar-web-plugin-2.3.jar) von
http://docs.sonarqube.org/display/SONAR/Web+Plugin, kopieren Sie sie in den
SonarQube-Plugin-Ordner (z.B. \Tools\SonarQube\extensions\plugins) und starten
Sie SonarQube neu.
Der Aufruf kann zum Beispiel folgendermaßen erfolgen:
cd \MeinWorkspace\<MeinWebProjekt>
mvn clean sonar:sonar -Dsonar.language=web Dsonar.dynamicAnalysis=false Dsonar.web.sourceDirectory=src/main/webapp -Dsonar.branch=Web
start http://localhost:9000
Probleme
1. Falls Sie die Ermittlung des RCI (Rules Compliance Index) vermissen:
Den gab es nur in früheren SonarQube-Versionen. In den aktuellen Versionen gibt es
stattdessen die "Technische Schuld" ("Technical Debt").
Falls Sie auch in aktuellen SonarQube-Versionen den RCI ermittelt haben wollen, gibt es
verschiedene Optionen, beispielsweise:
a) Sie können das sonar-rci-plugin von Robert Willems of Brilman installieren. Dann
finden Sie das RCI-Ergebnis unter der jeweiligen Projekte-Startseite unter dem
Tabulator-Menüpunkt "Measures".
b) Alternativ können Sie die Violations per SonarQube-REST-Schnittstelle (Web API)
abfragen und den RCI selbst berechnen. Dies leistet für SonarQube in der Version 6.3 das
Programm "SonarQube63Rci". Es liefert eine Liste der RCIs zu allen in SonarQube
berücksichtigten Projekten sowohl als HTML-Datei, sowie auch als CSV-Datei, welche
in OpenOffice Calc und Microsoft Excel geladen werden kann. Sie finden
"SonarQube63Rci" im Download der Maven-Projekte.
2. Falls Sie mit SonarQube serverseitige Probleme haben, sehen Sie sich die Logdateien an:
type \Tools\SonarQube\logs\*.log
3. Falls Sie mit Ihrem "Quality Profile" Probleme haben, z.B. die Fehlermeldung
[ERROR] Quality profile not found : 'Sonar way with Findbugs' on
language 'java'
dann stellen Sie sicher, dass es den Profilnamen gibt, der Profilname keine Sonderzeichen
und keine Leerzeichen enthält, und dass es nicht zweierlei SonarQube-Installationen auf
demselben PC gibt.
4. Falls Sie folgende Fehlermeldung erhalten:
[ERROR] Failed to execute goal org.sonarsource.scanner.maven:sonarmaven-plugin:3.2:sonar (default-cli) on project [...]: SCM provider was
set to "..." but no SCM provider found for this key. Supported SCM
providers are git,svn
oder
[ERROR] Failed to execute goal org.codehaus.mojo:sonar-mavenplugin:2.5:sonar (default-cli) on project [...]: SCM provider was set
to "[...]" but no provider found for this key. Supported providers are
git,svn
Dann können Sie den SCM-Sensor deaktivieren: Als "admin" anmelden, und dann je
nach Version:
entweder: "Administration" | "SCM" | "Disable the SCM Sensor": "True" | "Save",
oder: "Settings" | "General Settings" | "SCM" | "Disable the SCM Sensor" --> "True".
5. Falls Sie eine Oracle-Datenbank verwenden wollen und folgende Fehlermeldung
erhalten:
org.sonar.api.utils.MessageException: Oracle NLS_CHARACTERSET does not
support UTF8
Dann muss der Characterset in der Oracle-Datenbank auf AL32UTF8 umgestellt werden.
Überprüfen Sie dies mit dem SQL-Kommando "Select * from
nls_database_parameters;" und sehen Sie sich an: Installing the Database, Ändern
des Oracle-Datenbank-Character-Encodings.
6. Falls Sie folgende Fehlermeldung erhalten:
Der Dienst [...] konnte nicht gestartet werden [...] Der Prozess wurde
unerwartet beendet
Suchen Sie in der \Tools\SonarQube\logs\sonar.log nach genaueren
Fehlermeldungen.
Überprüfen Sie, ob Sie beim installierten Windows-Dienst korrekte WindowsAnmeldeinformationen eingetragen haben.
Eventuell müssen Sie in der \Tools\SonarQube\conf\wrapper.conf die Zeile
wrapper.java.command=java
ändern zu:
wrapper.java.command=C:\Program Files\Java\jdk1.7\bin\java.exe
7. Falls Sie unter 64-bit-Windows installieren und eine ältere Versionen von SonarQube
verwenden: Ältere Versionen vom SonarQube-Wrapper funktionieren eventuell nur mit
einem 32-bit-JDK. Dieses muss installiert (z.B. nach C:\Program Files
(x86)\Java\jdk1.7-32bit) und bei SonarQube eingetragen werden. Hierzu in der
\Tools\SonarQube\conf\wrapper.conf die Zeile
wrapper.java.command=java
ändern zu:
wrapper.java.command=C:\Program Files (x86)\Java\jdk1.7-32bit\bin\java
8. JaCoCo:
Falls Sie Probleme mit der Erfassung und Anzeige der Java Code Coverage der JUnitTests mit dem JaCoCo-Plug-in haben:
Prüfen Sie, ob im target-Verzeichnis die Datei jacoco.exec erzeugt wurde.
Beobachten Sie, ob im Kommandozeilenfenster angezeigt wird:
...
[INFO] Sensor JaCoCoSensor [java]
[INFO] Analysing .../target/jacoco.exec
[INFO] Sensor JaCoCoSensor [java] (done) | time=12ms
...
Falls Sie das maven-surefire-plugin oder das maven-failsafe-plugin verwenden,
beachten Sie die Hinweise zum JaCoCo-Maven Plug-in und zum prepare-agent-Goal.
Falls irgendwo (z.B. bei der Konfiguration vom maven-surefire-plugin oder mavenfailsafe-plugin) in einem Plug-in die Property argLine gesetzt wird, funktioniert die
JaCoCo-Parameterweitergabe nicht, dadurch wird die Datei jacoco.exec nicht erzeugt,
und Sie erhalten keine Code-Coverage-Ergebnisse. Um dies zu vermeiden, gibt es
verschiedene Optionen:
a) Sie können in den relevanten Plug-ins die argLine-Anweisungen überall (auch in
Parent-POMs) entfernen, und die gewünschten Properties global im properties-Block
definieren:
<properties>
<argLine>...</argLine>
</properties>
b) Alternativ können Sie in den Plug-ins den von JaCoCo benötigten argLine-Zusatz als
festen Wert der argLine-Property hinzufügen, indem Sie statt
<argLine>...</argLine>
schreiben:
<argLine>... javaagent:${settings.localRepository}/org/jacoco/org.jacoco.agent/0.7.9
/org.jacoco.agent-0.7.9runtime.jar=destfile=target/jacoco.exec</argLine>
Allerdings müssen Sie bei dieser Variante eventuell bei einem Versions-Upgrade den
benötigten Ausdruck erneut ermitteln (mit mvn -X ...) und anpassen.
c) Falls Sie keine Änderung in den POMs machen können, können Sie alternativ
folgenden Trick anwenden: Falls in den POMs beim maven-surefire-plugin eine
<argLine>...</argLine> definiert ist, können Sie den Kommandozeilenparameter
maven.surefire.debug missbrauchen, um im Testfall die argLine per Kommandozeile
zu erweitern (siehe hierzu surefire:test, debugForkedProcess): Unter Linux könnte dies
beispielsweise so aussehen (die zwei Kommandos in jeweils einer Zeile):
_MVN_LOCAL_REPO=$( mvn help:effective-settings | grep localRepository |
sed "s|<localRepository>||" | sed "s|</localRepository>||" | tr -d
'[:space:]' )
mvn clean install -Dmaven.surefire.debug="javaagent:$_MVN_LOCAL_REPO/org/jacoco/org.jacoco.agent/0.7.9/org.jacoco
.agent-0.7.9-runtime.jar=destfile=target/jacoco.exec"
org.jacoco:jacoco-maven-plugin:prepare-agent
org.sonarsource.scanner.maven:sonar-maven-plugin:3.2:sonar Dsonar.host.url=http://localhost:9000 -Dmaven.test.failure.ignore=true
Kontrollieren Sie das Ergebnis, indem Sie Maven mit -X im Debug-Modus starten, und
sich beim Start der Tests die Ausgabe der "Forking command line" ansehen.
Falls Sie das SonarQube-Kommando durch Jenkins ausführen lassen, und falls das lokale
Maven-Repository jeweils im Workspace-Verzeichnis angelegt wird, können Sie das
jeweilige Repo-Verzeichnis so ähnlich ermitteln wie:
"_MVN_LOCAL_REPO=$WORKSPACE/.repository" (vermeiden Sie dabei Leerzeichen in
den Pfaden).
d) Alternativ können Sie vermeiden, dass der vorherige argLine-Wert überschrieben
wird, wenn Sie in den Plug-ins statt
<argLine>...</argLine>
schreiben:
<argLine>... ${argLine}</argLine>
Falls das JaCoCo-Maven-Plug-in nicht für jedes Goal aktiv ist, kann dies allerdings zu
einer der folgenden Fehlermeldungen führen:
Fehler: Hauptklasse ${argLine} konnte nicht gefunden oder geladen
werden
Error: Could not find or load main class ${argLine}
In diesem Fall kann die Lösung sein, dass Sie über eine Kommandozeilenvariable
verschiedene Profile in der pom.xml aktivieren:
...
<profiles>
<profile>
<id>ohne-JaCoCo</id>
<activation>
<activeByDefault>true</activeByDefault>
<property>
<name>!JaCoCo</name>
</property>
</activation>
<properties>
<jacoco.argLine></jacoco.argLine>
</properties>
</profile>
<profile>
<id>SonarQube-mit-JaCoCo</id>
<activation>
<property>
<name>JaCoCo</name>
</property>
</activation>
<build>
<plugins>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.7.9</version>
<executions>
<execution>
<id>jacoco-initialize</id>
<goals>
<goal>prepare-agent</goal>
</goals>
<configuration>
<propertyName>jacoco.argLine</propertyName>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.12</version>
<configuration>
<argLine>-Duser.language=de -Duser.region=DE -Xmx512m Dfile.encoding=UTF-8 ${jacoco.argLine}</argLine>
</configuration>
</plugin>
</plugins>
</build>
...
So wird bei normalen Kommandos wie z.B. "mvn clean install" die Property
jacoco.argLine als Leerstring initialisiert, um die Fehlermeldung zu vermeiden. Für
SonarQube-Analysen inklusive JaCoCo-Plug-in führen Sie aus:
mvn clean install sonar:sonar -DJaCoCo
Dann entfällt die Vorinitialisierung der Property jacoco.argLine und das JaCoCo-Plugin wird aktiviert.
d) Weitere Ideen finden Sie unter: JaCoCo JVM args and Surefire JVM args together in
Maven.
9. Cobertura:
Falls Sie die Code Coverage by Unit Tests (Testabdeckung) nicht mit JaCoCo sondern
stattdessen mit Cobertura ermitteln wollen, verfahren Sie wie folgt.
Beachten Sie, dass Cobertura 2.1.1 und das cobertura-maven-plugin 2.7 nur bis Java 7
funktionieren.
Während für JaCoCo in SonarQube kein Plug-in benötigt wird, muss für Cobertura das
sonar-cobertura-plugin in einer passenden Version in SonarQube installiert werden.
Beispielsweise für SonarQube 6.3 wird sonar-cobertura-plugin-1.9.jar benötigt.
Downloaden Sie die passende Version von: https://github.com/galexandre/sonarcobertura/releases/.
Kopieren Sie die heruntergeladene sonar-cobertura-plugin-Jar-Datei (z.B. sonarcobertura-plugin-1.9.jar) in das extensions/plugins-Verzeichnis der SonarQubeInstallation (siehe hierzu
https://docs.sonarqube.org/display/SONAR/Installing+a+Plugin).
Starten Sie SonarQube neu (z.B. unter Linux per "bin/linux-x86-64/sonar.sh
restart").
Jetzt können Sie SonarQube-Reports inklusive Cobertura-Testabdeckungsergebnisse
erzeugen:
cd \MeinWorkspace\<MeinProjekt>
mvn clean install cobertura:cobertura -Dcobertura.report.format=xml
--> im target/site/cobertura-Verzeichnis entsteht die Datei coverage.xml.
mvn org.sonarsource.scanner.maven:sonar-maven-plugin:3.2:sonar Dsonar.host.url=http://localhost:9000
--> u.a. wird gemeldet:
...
[INFO] Sensor CoberturaSensor [cobertura]
[INFO] parsing .../target/site/cobertura/coverage.xml
[INFO] Sensor CoberturaSensor [cobertura] (done) | time=5ms
...
start http://localhost:9000
--> SonarQube zeigt die Testabdeckung an.
Wenn Sie wollen, können Sie die beiden Maven-Kommandos auch in einer einzigen
Zeile ausführen:
mvn clean install cobertura:cobertura
org.sonarsource.scanner.maven:sonar-maven-plugin:3.2:sonar Dcobertura.report.format=xml -Dsonar.host.url=http://localhost:9000
Falls Sie folgende Fehlermeldung erhalten:
Unable to register extension
org.sonar.plugins.cobertura.CoberturaSensor from plugin 'cobertura':
Lorg/sonar/api/scan/filesystem/ModuleFileSystem;:
org.sonar.api.scan.filesystem.ModuleFileSystem
Dann passt die Version vom sonar-cobertura-plugin nicht zur SonarQube-Version,
siehe hierzu: https://github.com/galexandre/sonar-cobertura/releases/. Für SonarQube 6.3
funktioniert sonar-cobertura-plugin-1.9.jar.
Erstellung von Javadoc- und Source-Archiven
Das folgende Beispiel zeigt, wie Sie mit dem "maven install"-Kommando nicht nur .jarArtefakte, sondern auch Javadoc und gezippten Sourcecode zu beliebigen Maven-Projekten
erzeugen. Dazu werden das Maven Javadoc Plugin und das Maven Source Plugin verwendet.
Falls Sie kein vorhandenes Maven-Projekt verwenden wollen, legen Sie folgendermaßen ein
neues an:
cd \MeinWorkspace
mvn archetype:generate -DinteractiveMode=false DgroupId=de.meinefirma.meinprojekt -DartifactId=MvnSourceUndJavadoc
cd MvnSourceUndJavadoc
Ersetzen Sie den Inhalt der pom.xml durch (bzw. fügen Sie bei anderen Projekten die <plugin>Blöcke ein):
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>de.meinefirma.meinprojekt</groupId>
<artifactId>MvnSourceUndJavadoc</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>MvnSourceUndJavadoc</name>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>2.10.4</version>
<executions>
<execution>
<id>attach-javadoc</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>3.0.1</version>
<executions>
<execution>
<id>attach-sources</id>
<goals>
<goal>jar-no-fork</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
Führen Sie aus:
cd \MeinWorkspace\MvnSourceUndJavadoc
mvn clean install
Die Ergebnisse werden im lokalen Maven-Repository generiert. Setzen Sie folgende
Environmentvariable entsprechend Ihres Repo-Pfades, um die Ergebnisse zu untersuchen,
beispielsweise:
set MVN_REPO=%USERPROFILE%\.m2\repository
oder
set MVN_REPO=D:\Tools\Maven3-Repo
Sehen Sie sich das Ergebnis in Ihrem lokalen Maven-Repository an:
dir %MVN_REPO%\de\meinefirma\meinprojekt\MvnSourceUndJavadoc\1.0-SNAPSHOT
Zusätzlich zu den üblichen .jar- und .pom-Dateien erhalten Sie eine ...-javadoc.jar und
eine ...-sources.jar.
Sehen Sie sich die Javadoc an (die beim Standardbeispiel natürlich nur sehr kurz ausfällt):
md \MeinWorkspace\MvnSourceUndJavadoc\javadoc
cd \MeinWorkspace\MvnSourceUndJavadoc\javadoc
jar xvf %MVN_REPO%\de\meinefirma\meinprojekt\MvnSourceUndJavadoc\1.0SNAPSHOT\MvnSourceUndJavadoc-1.0-SNAPSHOT-javadoc.jar
start index.html
Listen Sie den Inhalt der ...-sources.jar auf:
jar tvf %MVN_REPO%\de\meinefirma\meinprojekt\MvnSourceUndJavadoc\1.0SNAPSHOT\MvnSourceUndJavadoc-1.0-SNAPSHOT-sources.jar
Signaturen erzeugen
Falls Sie Artefakte übers Internet weitergeben wollen (oder z.B. in Maven Central verfügbar
machen wollen), empfiehlt es sich, Signaturen hinzuzufügen. Das folgende Beispiel erzeugt
Signaturen mit dem PGP Maven plugin.
Voraussetzung für das folgende Beispiel ist eine beliebige Private-Key-Schlüsseldatei und die
Passphrase hierzu. Falls Sie dies noch nicht haben und falls Sie GnuPG / Gpg4win installiert
haben, können Sie Schlüssel folgendermaßen erzeugen (Erläuterungen hierzu finden Sie unter
java-crypto.htm#GnuPG):
gpg2 --gen-key
1 2048 0 j Mein Name
geheime GPG-Passphrase 42
[email protected]
GPG-Test
f
Meine
gpg2 -ao GPG-Test-pubkey.asc --export "GPG-Test"
gpg2 -o GPG-Test-seckey.gpg --export-secret-keys "GPG-Test"
Hierbei entsteht die Private-Key-Schlüsseldatei GPG-Test-seckey.gpg und der Public-Key
GPG-Test-pubkey.asc.
Weiterhin benötigen Sie für das folgende Beispiel ein beliebiges Maven-Projekt. Falls Sie kein
vorhandenes verwenden wollen, legen Sie folgendermaßen ein neues an:
cd \MeinWorkspace
mvn archetype:generate -DinteractiveMode=false DgroupId=de.meinefirma.meinprojekt -DartifactId=MvnSign
cd MvnSign
Ersetzen Sie den Inhalt der pom.xml durch (bzw. fügen Sie bei anderen Projekten den <plugin>Block ein):
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>de.meinefirma.meinprojekt</groupId>
<artifactId>MvnSign</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>MvnSign</name>
<build>
<plugins>
<plugin>
<groupId>org.kohsuke</groupId>
<artifactId>pgp-maven-plugin</artifactId>
<version>1.1</version>
<executions>
<execution>
<goals>
<goal>sign</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
Führen Sie versuchsweise aus:
cd \MeinWorkspace\MvnSign
mvn clean install
Da Sie keine Schlüsseldatei angegeben haben, erhalten Sie die Fehlermeldung:
[ERROR] Failed to execute goal org.kohsuke:pgp-maven-plugin:1.1:sign
(default) on project MvnSign: No PGP secret key is configured. Either do so
in POM, or via -Dpgp.secretkey, or the PGP_SECRETKEY environment variable
Geben Sie die Private-Key-Schlüsseldatei und die Passphrase über eine der genannten Methoden
an, z.B. so (passen Sie die Ausdrücke an Ihre Schlüsseldatei an):
mvn clean install -Dpgp.secretkey=keyfile:GPG-Test-seckey.gpg Dpgp.passphrase="literal:Meine geheime GPG-Passphrase 42"
Um sich das Ergebnis ansehen zu können, setzen Sie zuerst Ihren Pfad zu Ihrem lokalen MavenRepository, beispielsweise:
set MVN_REPO=%USERPROFILE%\.m2\repository
oder
set MVN_REPO=D:\Tools\Maven3-Repo
Sehen Sie sich das Ergebnis an:
dir %MVN_REPO%\de\meinefirma\meinprojekt\MvnSign\1.0-SNAPSHOT
Zusätzlich zu den üblichen .jar- und .pom-Dateien erhalten Sie mehrere entsprechende .ascSignatur-Dateien, die mit "-----BEGIN PGP SIGNATURE-----" beginnen:
type %MVN_REPO%\de\meinefirma\meinprojekt\MvnSign\1.0-SNAPSHOT\MvnSign-1.0SNAPSHOT.jar.asc
Java-Programme (und andere Programme) mit dem exec-maven-plugin
ausführen
1. Öffnen Sie ein Kommandozeilenfenster, wechseln Sie in Ihr Projekte-Verzeichnis und
erzeugen Sie ein beliebiges Projekt:
cd \MeinWorkspace
mvn archetype:generate -DinteractiveMode=false DgroupId=de.meinefirma.meinprojekt -DartifactId=MvnExec
cd MvnExec
tree /F
2. Fügen Sie in der pom.xml vor dem <dependencies>-Block hinzu:
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.6.0</version>
<executions>
<execution>
<phase>test</phase>
<goals>
<goal>java</goal>
</goals>
<configuration>
<mainClass>de.meinefirma.meinprojekt.Timestamp</mainClass>
<arguments>
<argument>Timestamp:</argument>
<argument>yyyy-MM-dd HH:mm:ss,S</argument>
</arguments>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
Erzeugen Sie im src\main\java\de\meinefirma\meinprojekt-Verzeichnis
Klasse: Timestamp.java
die
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
package de.meinefirma.meinprojekt;
import java.text.SimpleDateFormat;
import java.util.Date;
public class Timestamp
{
public static void main( String[] args )
{
String prefix
= ( args.length > 0 ) ? args[0] :
"Timestamp:";
40.
String datetimePattern = ( args.length > 1 ) ? args[1] : "yyyyMM-dd HH:mm:ss,S";
41.
System.out.println( "\n" + prefix + " " + (new SimpleDateFormat(
datetimePattern ).format( new Date() )) + "\n" );
42.
}
43. }
44. So sieht Ihre Verzeichnisstruktur aus:
45.
46. [\MeinWorkspace\MvnExec]
47.
|- [src]
48.
|
|- [main]
49.
|
|
'- [java]
50.
|
|
'- [de]
51.
|
|
'- [meinefirma]
52.
|
|
'- [meinprojekt]
53.
|
|
|- App.java
54.
|
|
'- Timestamp.java
55.
|
'- [test]
56.
|
'- [java]
57.
|
'- [de]
58.
|
'- [meinefirma]
59.
|
'- [meinprojekt]
60.
|
'- AppTest.java
61.
'- pom.xml
62. Geben Sie im Kommandozeilenfenster ein:
cd \MeinWorkspace\MvnExec
mvn compile
mvn exec:java -Dexec.mainClass="de.meinefirma.meinprojekt.Timestamp"
mvn exec:java -Dexec.mainClass="de.meinefirma.meinprojekt.Timestamp" Dexec.args="'Anderer Prefix:' 'yyyy-MM-dd HH:mm'"
Sie erhalten zwei verschiedene Timestamp-Ausgaben mit verschiedener Formatierung.
Interessanter ist, dass die Ausführung des Java-Programms in der pom.xml an die Maventest-Lifecycle-Phase gekoppelt ist. Führen Sie aus:
mvn package
Auch diesmal erhalten Sie die Timestamp-Ausgabe.
63. Natürlich können Sie auch an andere Maven-Lifecycle-Phasen ankoppeln und beliebige
andere Java- oder andere Programme ausführen.
Sehen Sie sich die Doku zum exec-maven-plugin an.
Guice und AOP
Das folgende Beispiel zeigt den Einsatz von Google Guice für Dependency Injection (DI) und
für aspektorientierte Programmierung (AOP).
Das Beispiel demonstriert anhand der Berechnung von Fibonacci-Zahlen:






wie mit Guice.createInjector() ein DI-Injector erzeugt wird,
wie diesem Injector ein AOP-Modul hinzugefügt wird,
wie dieses AOP-Modul eine Annotation mit einem Method-Interceptor verknüpft,
wie eine geeignete Marker-Annotation implementiert wird (UseLoggerInterceptor),
wie ein Method-Interceptor für Logging implementiert wird (LoggerInterceptor) und
wie durch das einfache Hinzufügen der Annotation @UseLoggerInterceptor zu einer
Methode Logging aktiviert wird.
1. Wechseln Sie in Ihr Projekte-Verzeichnis und erzeugen Sie ein neues Projekt:
cd \MeinWorkspace
md MvnGuiceAop
cd MvnGuiceAop
md src\main\java\de\meinefirma\meinprojekt
2. Erzeugen Sie im Projektverzeichnis die Projektkonfigurationsdatei: pom.xml
3.
4. <project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
5.
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
6.
<modelVersion>4.0.0</modelVersion>
7.
<groupId>de.meinefirma.meinprojekt</groupId>
8.
<artifactId>MvnGuiceAop</artifactId>
9.
<version>1.0-SNAPSHOT</version>
10.
<build>
11.
<plugins>
12.
<plugin>
13.
<artifactId>maven-assembly-plugin</artifactId>
14.
<version>3.0.0</version>
15.
<configuration>
16.
<descriptorRefs>
17.
<descriptorRef>jar-with-dependencies</descriptorRef>
18.
</descriptorRefs>
19.
<archive>
20.
<manifest>
21.
<mainClass>de.meinefirma.meinprojekt.Main</mainClass>
22.
</manifest>
23.
</archive>
24.
</configuration>
25.
<executions>
26.
<execution>
27.
<id>make-assembly</id>
28.
<phase>package</phase>
29.
<goals>
30.
<goal>single</goal>
31.
</goals>
32.
</execution>
33.
</executions>
34.
</plugin>
35.
</plugins>
36.
</build>
37.
<dependencies>
38.
<dependency>
39.
<groupId>com.google.inject</groupId>
40.
<artifactId>guice</artifactId>
41.
<version>4.1.0</version>
42.
</dependency>
43.
<dependency>
44.
<groupId>aopalliance</groupId>
45.
<artifactId>aopalliance</artifactId>
46.
<version>1.0</version>
47.
</dependency>
48.
</dependencies>
49. </project>
50. Erzeugen Sie im src\main\java\de\meinefirma\meinprojekt-Verzeichnis folgende
fünf Klassen:
Main.java
package de.meinefirma.meinprojekt;
import com.google.inject.Guice;
import com.google.inject.Injector;
public class Main
{
public static void main( String[] args )
{
Injector inj = Guice.createInjector( new AopModule() );
Fibonacci fib = inj.getInstance( Fibonacci.class );
int n = 7;
System.out.println( "\nFibonacci( " + n + " ) ist " +
fib.fibonacci( n ) );
}
}
Fibonacci.java
package de.meinefirma.meinprojekt;
public class Fibonacci
{
@UseLoggerInterceptor
public long fibonacci( int n )
{
return ( n < 2 ) ? n : (fibonacci( n - 1 ) + fibonacci( n - 2 ));
}
}
AopModule.java
package de.meinefirma.meinprojekt;
import static com.google.inject.matcher.Matchers.annotatedWith;
import static com.google.inject.matcher.Matchers.any;
import com.google.inject.AbstractModule;
public class AopModule extends AbstractModule
{
protected void configure()
{
bindInterceptor( any(), annotatedWith( UseLoggerInterceptor.class
), new LoggerInterceptor() );
}
}
UseLoggerInterceptor.java
package de.meinefirma.meinprojekt;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention( RetentionPolicy.RUNTIME )
public @interface UseLoggerInterceptor
{
}
LoggerInterceptor.java
package de.meinefirma.meinprojekt;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import java.lang.reflect.Method;
import java.util.Arrays;
public class LoggerInterceptor implements MethodInterceptor
{
public Object invoke( MethodInvocation methInvoc ) throws Throwable
{
Method
mth = methInvoc.getMethod();
Object[] arg = methInvoc.getArguments();
String
cll = mth.getName() + Arrays.deepToString( arg );
System.out.print( cll + ": " );
Object result = methInvoc.proceed();
System.out.println( " " + cll + " --> " + result );
return result;
}
}
51. Geben Sie im Kommandozeilenfenster ein:
cd \MeinWorkspace\MvnGuiceAop
mvn package
java -jar target/MvnGuiceAop-1.0-SNAPSHOT-jar-with-dependencies.jar
Sie sehen zu jedem fibonacci()-Methodenaufruf jeweils eine Meldung vor und eine
Meldung nach dem Methodenaufruf.
Eigenes Maven-Plugin (Mojo)
In Java programmierte Maven-Plugins bestehen aus Mojos. Ein Mojo ("Maven (plain) old Java
Object") ist eine Java-Klasse die das Interface org.apache.maven.plugin.Mojo implementiert
(oder org.apache.maven.plugin.AbstractMojo erweitert) und damit ein Plugin-Goal
realisiert.
Siehe auch guide-java-plugin-development, maven-plugin-api und mojo-api-specification.
Mojo mit Parameter
1. Öffnen Sie ein Kommandozeilenfenster, wechseln Sie in Ihr Projekte-Verzeichnis (z.B.
\MeinWorkspace) und erzeugen Sie ein Mojo-Projekt:
cd \MeinWorkspace
mvn archetype:generate -DinteractiveMode=false DarchetypeArtifactId=maven-archetype-mojo DgroupId=de.meinefirma.meinprojekt -DartifactId=MvnTimestampPlugin
cd MvnTimestampPlugin
2. Sehen Sie sich die generierte pom.xml an und beachten Sie insbesondere das
<packaging> und die <dependency>:
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
...
<packaging>maven-plugin</packaging>
...
<dependencies>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-plugin-api</artifactId>
<version>2.2.1</version>
</dependency>
...
Löschen Sie im Verzeichnis src\main\java\de\meinefirma\meinprojekt die
MyMojo.java und legen Sie stattdessen in diesem Verzeichnis die Mojo-Datei
TimestampMojo.java mit folgendem Inhalt an:
package de.meinefirma.meinprojekt;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.apache.maven.plugin.AbstractMojo;
/**
* @goal timestamp
*/
public class TimestampMojo extends AbstractMojo
{
/** @parameter */ String prefix = "Timestamp:";
/** @parameter */ String datetimePattern = "yyyy-MM-dd HH:mm:ss,S";
@Override
public void execute()
{
getLog().info( prefix + " " +
(new SimpleDateFormat( datetimePattern ).format( new Date()
)) );
35.
}
36. }
Bitte beachten Sie die für das Maven-Plugin wichtigen Angaben @goal... und
@parameter....
Sie können das Goal über @phase auch an eine bestimmte Lifecycle-Phase binden.
Weiteres zu @phase, @goal, @parameter und weiteren Annotationen finden Sie unter
AbstractMojo, Mojo API Specification und Guide to Developing Java Plugins.
37. Die Projektstruktur sieht jetzt so aus:
cd \MeinWorkspace\MvnTimestampPlugin
tree /F
[\MeinWorkspace\MvnTimestampPlugin]
|- [src]
|
'- [main]
|
'- [java]
|
'- [de]
|
'- [meinefirma]
|
'- [meinprojekt]
|
'- TimestampMojo.java
'- pom.xml
38. Die allgemeine Syntax, um ein Goal auf der Kommandozeile auszuführen, lautet:
"mvn groupID:artifactID:[version:]goal" (der Versionsteil kann manchmal
weggelassen werden).
Bauen Sie das Projekt und führen Sie für einen ersten Test das Goal per Kommandozeile
aus:
cd \MeinWorkspace\MvnTimestampPlugin
mvn clean install
mvn de.meinefirma.meinprojekt:MvnTimestampPlugin:1.0-SNAPSHOT:timestamp
Sie erhalten:
[INFO] --- MvnTimestampPlugin:1.0-SNAPSHOT:timestamp (default-cli) @
MvnTimestampPlugin --[INFO] Timestamp: 2010-10-09 20:42:37,90
39. Interessant wird es jedoch erst, wenn Sie das neue Maven-Plugin in anderen Projekten
verwenden und beispielsweise mit Lifecycle-Phasen verknüpfen. Erweitern Sie die
pom.xml irgendeines beliebigen Projekts (z.B. MvnJaxbApp) im <build><plugins>...Block um folgendes <plugin>-Element:
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
<plugin>
<groupId>de.meinefirma.meinprojekt</groupId>
<artifactId>MvnTimestampPlugin</artifactId>
<version>1.0-SNAPSHOT</version>
<configuration>
<prefix>+++</prefix>
<datetimePattern>HH:mm:ss</datetimePattern>
</configuration>
<executions>
<execution>
<id>nach clean</id>
<phase>clean</phase>
<goals>
<goal>timestamp</goal>
</goals>
</execution>
<execution>
<id>nach compile</id>
<phase>compile</phase>
60.
61.
62.
63.
64.
65.
<goals>
<goal>timestamp</goal>
</goals>
</execution>
</executions>
</plugin>
Außer der Verknüpfung mit den beiden Lifecycle-Phasen clean und compile findet auch
die Konfiguration der beiden Parameter prefix und datetimePattern statt.
Führen Sie jetzt im Projektverzeichnis dieses anderen Projekts aus:
cd \MeinWorkspace\MvnJaxbApp-mit-TimestampMojo [anpassen!]
mvn clean package
Sie erhalten:
...
[INFO] --...
[INFO] --MvnJaxbApp
[INFO] +++
...
[INFO] --MvnJaxbApp
...
[INFO] --MvnJaxbApp
[INFO] +++
...
maven-clean-plugin:2.5:clean (default-clean) @ MvnJaxbApp -MvnTimestampPlugin:1.0-SNAPSHOT:timestamp (nach clean) @
--20:52:03
maven-compiler-plugin:3.6.1:compile (default-compile) @
--MvnTimestampPlugin:1.0-SNAPSHOT:timestamp (nach compile) @
--20:52:05
Mojo-PluginContext
1. Wir wollen das Plugin-Mojo so erweitern, dass es nicht nur die aktuelle Zeit anzeigt,
sondern zusätzlich die Dauer vom letzten Aufruf bis zu diesem Aufruf berechnet. Dazu
ist eine Kommunikation zwischen den verschiedenen Plugin-Aufrufen notwendig. Eine
solche Kommunikation (auch zwischen verschiedenen Plugins) ist über die
"PluginContext"-Map möglich.
Ersetzen Sie im MvnTimestampPlugin-Projekt im Unterverzeichnis
src\main\java\de\meinefirma\meinprojekt den Inhalt der TimestampMojo.java
durch Folgendes:
2.
3.
4.
5.
6.
7.
8.
package de.meinefirma.meinprojekt;
import java.text.SimpleDateFormat;
import java.util.*;
import org.apache.maven.plugin.AbstractMojo;
9. /**
10.
* @goal timestamp
11.
*/
12. public class TimestampMojo extends AbstractMojo
13. {
14.
/** @parameter */ String prefix = "Timestamp:";
15.
/** @parameter */ String datetimePattern = "yyyy-MM-dd HH:mm:ss,S";
16.
17.
@Override
18.
public void execute()
19.
{
20.
final String ctxTimeKey = "TimestampMojo-Time";
21.
Date date = new Date();
22.
Map
ctx = getPluginContext();
23.
Long timeZuletzt = (Long) ctx.get( ctxTimeKey );
24.
ctx.put( ctxTimeKey, new Long( date.getTime() ) );
25.
String dauer = ( timeZuletzt == null ) ? "" :
26.
", Dauer: " + (date.getTime() - timeZuletzt.longValue()) +
" ms";
27.
getLog().info( prefix + " " +
28.
(new SimpleDateFormat( datetimePattern ).format( date )) +
dauer );
29.
}
30. }
31. Bauen Sie das Timestamp-Plugin neu:
cd \MeinWorkspace\MvnTimestampPlugin
mvn clean install
Führen Sie wieder im Projektverzeichnis des anderen Projekts aus:
cd \MeinWorkspace\MvnJaxbApp-mit-TimestampMojo [anpassen!]
mvn clean package
Diesmal ist die Ausgabe erweitert um die Anzeige der "Dauer":
...
[INFO] --...
[INFO] --MvnJaxbApp
[INFO] +++
...
[INFO] --MvnJaxbApp
...
[INFO] --MvnJaxbApp
[INFO] +++
...
maven-clean-plugin:2.5:clean (default-clean) @ MvnJaxbApp -MvnTimestampPlugin:1.0-SNAPSHOT:timestamp (nach clean) @
--10:48:00
maven-compiler-plugin:3.6.1:compile (default-compile) @
--MvnTimestampPlugin:1.0-SNAPSHOT:timestamp (nach compile) @
--10:48:01, Dauer: 1234 ms
Mojo-JUnit-Test
1. Es gibt verschiedene Verfahren, um Maven-Plugins automatisiert testen zu können.
Einige sind beschrieben unter Introduction / Testing Styles.
2. Um das maven-plugin-testing-harness-Plugin verwenden zu können, erweitern Sie die
pom.xml des MvnTimestampPlugins im <dependencies>...-Block um folgendes
<dependency>-Element:
3.
4.
5.
6.
7.
8.
9.
<dependency>
<groupId>org.apache.maven.shared</groupId>
<artifactId>maven-plugin-testing-harness</artifactId>
<version>1.1</version>
<scope>test</scope>
</dependency>
10. Erzeugen Sie folgendermaßen weitere Unterverzeichnisse:
cd \MeinWorkspace\MvnTimestampPlugin
md src\test\resources
md src\test\java\de\meinefirma\meinprojekt
11. Für den Test wird nur eine einfache abgespeckte POM benötigt. Erzeugen Sie im
src\test\resources-Verzeichnis die Datei test-pom.xml mit folgendem Inhalt:
12.
13. <project>
14.
<modelVersion>4.0.0</modelVersion>
15.
<groupId>test</groupId>
16.
<artifactId>Test</artifactId>
17.
<build>
18.
<plugins>
19.
<plugin>
20.
<groupId>de.meinefirma.meinprojekt</groupId>
21.
<artifactId>MvnTimestampPlugin</artifactId>
22.
<configuration>
23.
<prefix>+++</prefix>
24.
<datetimePattern>HH:mm:ss</datetimePattern>
25.
</configuration>
26.
</plugin>
27.
</plugins>
28.
</build>
29. </project>
30. Erzeugen Sie im src\test\java\de\meinefirma\meinprojekt-Verzeichnis
TimestampMojoTest.java mit folgendem Inhalt:
31.
32. package de.meinefirma.meinprojekt;
33.
34. import java.io.File;
35. import java.util.*;
36. import org.apache.maven.plugin.logging.SystemStreamLog;
37. import org.apache.maven.plugin.testing.AbstractMojoTestCase;
38.
39. public class TimestampMojoTest extends AbstractMojoTestCase
die Datei
40. {
41.
public void testTimestampMojo() throws Exception
42.
{
43.
Map pluginContext = new HashMap();
44.
String log1 = executeMojo( pluginContext );
45.
String log2 = executeMojo( pluginContext );
46.
assertTrue( log1.length() < log2.length() );
47.
assertTrue( !log1.contains( "Dauer" ) );
48.
assertTrue( log2.contains( "Dauer" ) );
49.
}
50.
51.
private String executeMojo( Map pluginContext ) throws Exception
52.
{
53.
String
testPom = getBasedir() +
"/src/test/resources/test-pom.xml";
54.
String
artifactId = "MvnTimestampPlugin";
55.
StringBuffer log = new StringBuffer();
56.
TimestampMojo mojo = new TimestampMojo();
57.
configureMojo( mojo, artifactId, new File( testPom ) );
58.
mojo.setPluginContext( pluginContext );
59.
mojo.setLog( new TestLog( log ) );
60.
mojo.execute();
61.
String prefix = (String) getVariableValueFromObject( mojo,
"prefix" );
62.
assertNotNull( prefix );
63.
assertEquals( prefix, log.substring( 0, prefix.length() ) );
64.
return log.toString();
65.
}
66.
67.
class TestLog extends SystemStreamLog
68.
{
69.
StringBuffer log;
70.
71.
public TestLog( StringBuffer log )
72.
{
73.
this.log = log;
74.
}
75.
76.
// @Override
77.
public void info( CharSequence content )
78.
{
79.
log.append( content );
80.
}
81.
}
82. }
Die Variable testPom muss den Pfad zu einer geeigneten POM-Datei enthalten, in
welcher das Plugin MvnTimestampPlugin eingetragen ist.
TimestampMojoTest erweitert AbstractMojoTestCase, damit die Methoden
getBasedir(), configureMojo() und getVariableValueFromObject() zur
Verfügung stehen.
Bitte beachten Sie die Injizierung der Context-Map über setPluginContext() und des
Testloggers über setLog(). Letzteres wird benötigt, um die log-Ausgaben abzufangen.
executeMojo()
wird zweimal aufgerufen: Beim ersten Mal wird nur der Timestamp
geloggt. Beim zweiten Mal wird auch die Dauer geloggt, was durch "assertTrue(
log2.contains( "Dauer" ) )" überprüft wird.
Bitte beachten Sie, dass TestLog nur eine einzige der vielen Log-Methoden überschreibt,
was für diesen Test reicht, aber für andere Tests eventuell zu wenig sein kann.
83. Ihre Verzeichnisstruktur sieht jetzt so aus:
cd \MeinWorkspace\MvnTimestampPlugin
tree /F
-->
[\MeinWorkspace\MvnTimestampPlugin]
|- [src]
|
|- [main]
|
|
'- [java]
|
|
'- [de]
|
|
'- [meinefirma]
|
|
'- [meinprojekt]
|
|
'- TimestampMojo.java
|
'- [test]
|
|- [java]
|
|
'- [de]
|
|
'- [meinefirma]
|
|
'- [meinprojekt]
|
|
'- TimestampMojoTest.java
|
'- [resources]
|
'- test-pom.xml
'- pom.xml
84. Führen Sie den JUnit-Test aus:
cd \MeinWorkspace\MvnTimestampPlugin
mvn test
Mojo-Hilfstexte
1. Erweitern Sie die pom.xml um erläuternde Hilfstexte, damit folgendes Kommando Hilfe
bieten kann:
mvn help:describe Dplugin=de.meinefirma.meinprojekt:MvnTimestampPlugin:1.0-SNAPSHOT Ddetail
Test-Jar
Manchmal werden nur für Tests benötigte wichtige Testhilfsklassen auch in anderen MavenModulen benötigt. Die Weitergabe von Testhilfsklassen an andere Module wird durch das
maven-jar-plugin über das test-jar-Goal unterstützt, wie das folgende Beispiel zeigt (siehe
auch: Guide to using attached tests).
1. Wechseln Sie in Ihr Projekte-Verzeichnis und erzeugen Sie ein neues Maven-Projekt:
cd \MeinWorkspace
mvn archetype:generate -DinteractiveMode=false DgroupId=de.meinefirma.meinprojekt -DartifactId=MvnTestJar1
cd MvnTestJar1
2. Erzeugen Sie im src\test\java\de\meinefirma\meinprojekt-Verzeichnis die
wichtige Testhilfsklasse: MeinWichtigesTestUtil.java
3.
4. package de.meinefirma.meinprojekt;
5.
6. public class MeinWichtigesTestUtil
7. {
8.
public static int hilfsmethode( int a, int b )
9.
{
10.
return a * b;
11.
}
12. }
13. Ersetzen Sie im src\test\java\de\meinefirma\meinprojekt-Verzeichnis den Inhalt
der AppTest.java durch:
14.
15. package de.meinefirma.meinprojekt;
16.
17. import junit.framework.Assert;
18. import junit.framework.TestCase;
19.
20. public class AppTest extends TestCase
21. {
22.
public void testApp()
23.
{
24.
Assert.assertEquals( 6, MeinWichtigesTestUtil.hilfsmethode( 2, 3
) );
25.
}
26. }
27. Führen Sie im Kommandozeilenfenster den JUnit-Test aus und installieren Sie das
MvnTestJar1-Ergebnisartefakt im lokalen Maven-Repository (passen Sie den Pfad zum
lokalen Maven-Repository an):
mvn test
mvn clean install
Die Ergebnisse werden im lokalen Maven-Repository generiert. Setzen Sie folgende
Environmentvariable entsprechend Ihres Repo-Pfades, um die Ergebnisse zu untersuchen,
beispielsweise:
set MVN_REPO=%USERPROFILE%\.m2\repository
oder
set MVN_REPO=D:\Tools\Maven3-Repo
Sehen Sie sich an:
dir %MVN_REPO%\de\meinefirma\meinprojekt\MvnTestJar1\1.0-SNAPSHOT
Sie erhalten u.a.:
MvnTestJar1-1.0-SNAPSHOT.jar
28. Erzeugen Sie ein zweites Maven-Projekt:
cd \MeinWorkspace
mvn archetype:generate -DinteractiveMode=false DgroupId=de.meinefirma.meinprojekt -DartifactId=MvnTestJar2
cd MvnTestJar2
copy
..\MvnTestJar1\src\test\java\de\meinefirma\meinprojekt\AppTest.java
src\test\java\de\meinefirma\meinprojekt /Y
mvn test
Da die Dependency zu MvnTestJar1 fehlt, erhalten Sie erwartungsgemäß die
Fehlermeldung:
[ERROR] COMPILATION ERROR : ...\AppTest.java: cannot find ...
MeinWichtigesTestUtil
29. Erweitern Sie die pom.xml des neuen MvnTestJar2-Projekts um eine Dependency zum
MvnTestJar1-Projekt, indem Sie folgenden Block einfügen:
30.
31.
32.
33.
34.
35.
36.
<dependency>
<groupId>de.meinefirma.meinprojekt</groupId>
<artifactId>MvnTestJar1</artifactId>
<version>1.0-SNAPSHOT</version>
<scope>test</scope>
</dependency>
37. Wenn Sie erneut "mvn test" ausführen, erhalten Sie weiterhin die Fehlermeldung:
[ERROR] COMPILATION ERROR : ...\AppTest.java: cannot find ...
MeinWichtigesTestUtil
Der Grund ist, dass die Testhilfsklasse MeinWichtigesTestUtil nicht im
Auslieferartefakt MvnTestJar1-1.0-SNAPSHOT.jar enthalten ist, da sie nur für JUnitTests benötigt wird.
38. Jetzt kommt der Trick dieses Programmierbeispiels:
Um wichtige Testhilfsklassen auch in Tests in anderen Modulen verwenden zu können,
werden sie in gesonderten Auslieferartefakten weitergegeben, wie im Folgenden
beschrieben wird.
39. Ändern Sie in der pom.xml des neuen MvnTestJar2-Projekts folgendermaßen die
MvnTestJar1-Dependency:
40.
41.
42.
43.
44.
45.
46.
47.
<dependency>
<groupId>de.meinefirma.meinprojekt</groupId>
<artifactId>MvnTestJar1</artifactId>
<version>1.0-SNAPSHOT</version>
<type>test-jar</type>
<scope>test</scope>
</dependency>
Beachten Sie die "<type>test-jar</type>"-Zeile.
48. Fügen Sie in der pom.xml des ersteren MvnTestJar1-Projekts folgendermaßen das
maven-jar-plugin mit dem test-jar-Goal hinzu:
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.0.2</version>
<executions>
<execution>
<goals>
<goal>test-jar</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
Führen Sie im MvnTestJar1-Projekt erneut "mvn install" aus:
cd ..\MvnTestJar1
mvn install
dir %MVN_REPO%\de\meinefirma\meinprojekt\MvnTestJar1\1.0-SNAPSHOT
Diesmal erhalten Sie zwei Ergebnis-Jar-Archive:
MvnTestJar1-1.0-SNAPSHOT-tests.jar
MvnTestJar1-1.0-SNAPSHOT.jar
67. Jetzt funktionieren auch im MvnTestJar2-Projekt die Tests (inkl.
MeinWichtigesTestUtil):
cd ..\MvnTestJar2
mvn test
68. Das maven-jar-plugin kann noch Vieles mehr, siehe beispielsweise: Ausführbare JarDatei und REST-Client mit REST-JUnit-Test.
JUnit-Tests mit JUnit 5
Eine Alternative zu JUnit 4 ist das modernere JUnit 5. Es nutzt die Vorteile von Java 8, z.B. die
Funktionale Programmierung mit Lambda-Ausdrücken und das Stream-API.
1. Wechseln Sie in Ihr Projekte-Verzeichnis und erzeugen Sie ein neues Projekt:
cd \MeinWorkspace
md MvnJUnit5
cd MvnJUnit5
md src\main\java\de\meinefirma\meinprojekt
md src\test\java\de\meinefirma\meinprojekt
tree /F
2. Erstellen Sie im MvnJUnit5-Projektverzeichnis die Projektkonfigurationsdatei: pom.xml
3.
4. <project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
5.
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
6.
<modelVersion>4.0.0</modelVersion>
7.
<groupId>de.meinefirma.meinprojekt</groupId>
8.
<artifactId>MvnJUnit5</artifactId>
9.
<version>1.0-SNAPSHOT</version>
10.
<build>
11.
<plugins>
12.
<plugin>
13.
<groupId>org.apache.maven.plugins</groupId>
14.
<artifactId>maven-compiler-plugin</artifactId>
15.
<version>3.6.1</version>
16.
<configuration>
17.
<source>8</source>
18.
<target>8</target>
19.
</configuration>
20.
</plugin>
21.
</plugins>
22.
</build>
23.
<dependencies>
24.
<dependency>
25.
<groupId>org.junit.jupiter</groupId>
26.
<artifactId>junit-jupiter-api</artifactId>
27.
<version>5.0.0-M4</version>
28.
<scope>test</scope>
29.
</dependency>
30.
<dependency>
31.
<groupId>org.junit.jupiter</groupId>
32.
<artifactId>junit-jupiter-engine</artifactId>
33.
<version>5.0.0-M4</version>
34.
<scope>test</scope>
35.
</dependency>
36.
<dependency>
37.
<groupId>org.junit.platform</groupId>
38.
<artifactId>junit-platform-runner</artifactId>
39.
<version>1.0.0-M4</version>
40.
<scope>test</scope>
41.
</dependency>
42.
</dependencies>
43. </project>
Überprüfen Sie unter junit-jupiter-api, ob es mittlerweile eine neuere JUnit-5-Version
gibt.
44. Erstellen Sie im src\main\java\de\meinefirma\meinprojekt-Verzeichnis die
Anwendungsklasse: App.java
45.
46. package de.meinefirma.meinprojekt;
47.
48. public class App
49. {
50.
public static void main( String[] args ) {
51.
if( args != null && args.length > 1 ) {
52.
System.out.println( args[0] + " / " + args[1] + " = " +
53.
calc( Long.valueOf( args[0] ), Long.valueOf( args[1] )
) );
54.
}
55.
}
56.
57.
public static long calc( long val1, long val2 ) {
58.
return val1 / val2;
59.
}
60. }
61. Erstellen Sie im src\test\java\de\meinefirma\meinprojekt-Verzeichnis die JUnit5Testklasse: AppTest.java
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
package de.meinefirma.meinprojekt;
import
import
import
import
import
import
java.time.Duration;
java.util.stream.Stream;
org.junit.jupiter.api.*;
org.junit.platform.runner.JUnitPlatform;
org.junit.runner.RunWith;
static org.junit.jupiter.api.Assertions.*;
@RunWith( JUnitPlatform.class )
public class AppTest
{
@BeforeAll
static void beforeAll() {
System.out.println( "Einmalig vor allen Tests ..." );
}
@AfterAll
static void afterAll() {
System.out.println( "... fertig" );
}
@BeforeEach
void beforeEach() {
System.out.println( "Vor jedem einzelnen Test ..." );
}
@Test @DisplayName( "Teste App.main() mit Timeout" )
void testeAppMain() {
assertTimeoutPreemptively( Duration.ofMillis( 500 ), () ->
App.main( new String[] { "7", "3" } ) );
93.
}
94.
95.
@Test @DisplayName( "Teste 3 / 2" )
96.
void testeCalc() {
97.
System.out.println( "3 / 2 ..." );
98.
assertEquals( 1, App.calc( 3, 2 ), "Ergebnis muss 1 sein" );
99.
}
100.
101.
@Test @DisplayName( "Teste Exception nach Division by zero" )
102.
void testeExeption() {
103.
System.out.println( "1 / 0 ..." );
104.
App app = new App();
105.
ArithmeticException ex = assertThrows(
ArithmeticException.class, () -> App.calc( 1, 0 ) );
106.
assertEquals( "/ by zero", ex.getMessage(), "Exception-Message"
);
107.
}
108.
109.
@Test @DisplayName( "Mehrere Tests zusammengefasst" )
110.
void testeAssertAll() {
111.
System.out.println( "20 / ?? ..." );
112.
113.
114.
115.
116.
117.
118.
119.
120.
121.
"i="
122.
123.
124. }
assertAll( "Mehrere Tests",
() -> assertEquals( 6, App.calc( 20, 3 ) ),
() -> assertEquals( 5, App.calc( 20, 4 ) ),
() -> assertEquals( 4, App.calc( 20, 5 ) ) );
}
@TestFactory @DisplayName( "DynamicTest mit TestFactory" )
Stream<DynamicTest> testeDynamicTest() {
System.out.println( "1, 2, 3 / 2 ..." );
return Stream.of( 1, 2, 3 ).map( i -> DynamicTest.dynamicTest(
+ i,
() -> assertTrue( i > App.calc( i, 2 ) ) ) );
}
Sehen Sie sich zu den JUnit-5-Ausdrücken die JUnit-5-API-Doku an.
125.
Ihr Projekteverzeichnis sieht jetzt so aus:
126.
127.
128.
129.
130.
131.
132.
133.
134.
135.
136.
137.
138.
139.
140.
141.
[\MeinWorkspace\MvnJUnit5]
|- [src]
|
|- [main]
|
|
'- [java]
|
|
'- [de]
|
|
'- [meinefirma]
|
|
'- [meinprojekt]
|
|
'- App.java
|
'- [test]
|
'- [java]
|
'- [de]
|
'- [meinefirma]
|
'- [meinprojekt]
|
'- AppTest.java
'- pom.xml
142.
Führen Sie die fünf Testmethoden aus:
cd \MeinWorkspace\MvnJUnit5
mvn test
Sie erhalten beispielsweise (die Reihenfolge kann variieren):
------------------------------------------------------T E S T S
------------------------------------------------------Running de.meinefirma.meinprojekt.AppTest
Einmalig vor allen Tests ...
Vor jedem einzelnen Test ...
7 / 3 = 2
Vor jedem einzelnen Test ...
3 / 2 ...
Vor jedem einzelnen Test ...
1 / 0 ...
Vor jedem einzelnen Test ...
20 / ?? ...
Vor jedem einzelnen Test ...
1, 2, 3 / 2 ...
... fertig
Tests run: 7, Failures: 0, Errors: 0, Skipped: 0
[INFO] ----------------------------------------------------------------------[INFO] BUILD SUCCESS
[INFO] -----------------------------------------------------------------------
143.
Die per @DisplayName() gesetzten Texte werden beispielsweise angezeigt, wenn
Sie die Tests in JetBrains IntelliJ IDEA ausführen.
Parallelisierte Testausführung mit TestNG
Eine weitere Alternative zu JUnit ist TestNG. Hierzu gibt es auch Unterstützung durch das
bereits bekannte maven-surefire-plugin, womit im Folgenden die Parallelisierung von Tests
demonstriert wird.
1. Wechseln Sie in Ihr Projekte-Verzeichnis und erzeugen Sie ein neues Projekt:
cd \MeinWorkspace
mvn archetype:generate -DinteractiveMode=false DgroupId=de.meinefirma.meinprojekt -DartifactId=MvnTestNG
cd MvnTestNG
tree /F
[\MeinWorkspace\MvnTestNG]
|- [src]
|
|- [main]
|
|
'- [java]
|
|
'- [de]
|
|
'- [meinefirma]
|
|
'- [meinprojekt]
|
|
'- App.java
|
'- [test]
|
'- [java]
|
'- [de]
|
'- [meinefirma]
|
'- [meinprojekt]
|
'- AppTest.java
'- pom.xml
2. Ersetzen Sie den Inhalt der pom.xml durch:
3.
4. <project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
5.
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
6.
<modelVersion>4.0.0</modelVersion>
7.
<groupId>de.meinefirma.meinprojekt</groupId>
8.
<artifactId>MvnTestNG</artifactId>
9.
<version>1.0-SNAPSHOT</version>
10.
<packaging>jar</packaging>
11.
<name>MvnTestNG</name>
12.
<build>
13.
<plugins>
14.
<plugin>
15.
<groupId>org.apache.maven.plugins</groupId>
16.
<artifactId>maven-surefire-plugin</artifactId>
17.
<version>2.20</version>
18.
<configuration>
19.
<parallel>methods</parallel>
20.
<threadCount>10</threadCount>
21.
</configuration>
22.
</plugin>
23.
<plugin>
24.
<groupId>org.apache.maven.plugins</groupId>
25.
<artifactId>maven-compiler-plugin</artifactId>
26.
<version>3.6.1</version>
27.
<configuration>
28.
<source>1.7</source>
29.
<target>1.7</target>
30.
</configuration>
31.
</plugin>
32.
</plugins>
33.
</build>
34.
<dependencies>
35.
<dependency>
36.
<groupId>org.testng</groupId>
37.
<artifactId>testng</artifactId>
38.
<version>6.11</version>
39.
<scope>test</scope>
40.
</dependency>
41.
</dependencies>
42. </project>
43. Ersetzen Sie im src\main\java\de\meinefirma\meinprojekt-Verzeichnis
der App.java durch:
44.
45. package de.meinefirma.meinprojekt;
46.
47. import java.text.SimpleDateFormat;
48. import java.util.Date;
49.
50. public class App
51. {
52.
public static void main( String[] args ) throws
InterruptedException
53.
{
den Inhalt
54.
final SimpleDateFormat dfHhMmSsS = new SimpleDateFormat(
"HH:mm:ss.S" );
55.
String s = ( args != null && args.length > 0 ) ? args[0] : ".";
56.
System.out.println( "\n" + dfHhMmSsS.format( new Date() ) + " "
+ Thread.currentThread().getName() + " Start" );
57.
for( int i = 0; i < 10; i++ ) {
58.
Thread.sleep( 100 );
59.
System.out.print( s );
60.
Thread.sleep( 100 );
61.
}
62.
System.out.println( "\n" + dfHhMmSsS.format( new Date() ) + " "
+ Thread.currentThread().getName() + " Ende" );
63.
}
64. }
65. Löschen Sie im src\test\java\de\meinefirma\meinprojekt-Verzeichnis die
AppTest.java und legen Sie in diesem Verzeichnis folgende Testklassen an:
App1Test.java
package de.meinefirma.meinprojekt;
import org.testng.annotations.Test;
public class App1Test
{
@Test public void testApp() throws InterruptedException
{
App.main( new String[] { "1" } );
}
}
App2Test.java
package de.meinefirma.meinprojekt;
import org.testng.annotations.Test;
public class App2Test
{
@Test public void testApp() throws InterruptedException
{
App.main( new String[] { "2" } );
}
}
App3Test.java
package de.meinefirma.meinprojekt;
import org.testng.annotations.Test;
public class App3Test
{
@Test public void testApp3() throws InterruptedException
{
App.main( new String[] { "3" } );
}
@Test public void testApp4() throws InterruptedException
{
App.main( new String[] { "4" } );
}
@Test public void testApp5() throws InterruptedException
{
App.main( new String[] { "5" } );
}
}
66. Ihr Projekteverzeichnis sieht jetzt so aus:
67.
68. [\MeinWorkspace\MvnTestNG]
69.
|- [src]
70.
|
|- [main]
71.
|
|
'- [java]
72.
|
|
'- [de]
73.
|
|
'- [meinefirma]
74.
|
|
'- [meinprojekt]
75.
|
|
'- App.java
76.
|
'- [test]
77.
|
'- [java]
78.
|
'- [de]
79.
|
'- [meinefirma]
80.
|
'- [meinprojekt]
81.
|
'- App1Test.java
82.
|
'- App2Test.java
83.
|
'- App3Test.java
84.
'- pom.xml
85. Führen Sie die fünf Testmethoden in den drei Testklassen aus:
cd \MeinWorkspace\MvnTestNG
mvn test
Sie erhalten beispielsweise:
------------------------------------------------------T E S T S
------------------------------------------------------11:22:33.0 pool-1-thread-1 Start
11:22:33.0 pool-1-thread-4 Start
11:22:33.0 pool-1-thread-2 Start
11:22:33.0 pool-1-thread-5 Start
11:22:33.0 pool-1-thread-3 Start
31245134521342513245312541354231524135423154231254
11:22:35.0
11:22:35.0
11:22:35.0
11:22:35.0
11:22:35.0
pool-1-thread-1
pool-1-thread-5
pool-1-thread-3
pool-1-thread-2
pool-1-thread-4
Ende
Ende
Ende
Ende
Ende
Tests run: 5, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 2.1 sec
[INFO] ----------------------------------------------------------------------[INFO] BUILD SUCCESS
[INFO] -----------------------------------------------------------------------
Wie Sie gut erkennen können, werden die fünf Testmethoden in fünf parallelen Threads
ausgeführt.
Bitte beachten Sie: Durch die Einstellung <parallel>methods</parallel> werden
nicht nur Klassen, sondern auch die Methoden innerhalb der Klasse App3Test.java
parallel ausgeführt.
86. Ersetzen Sie testweise in der pom.xml die Zeile
87.
88.
<parallel>methods</parallel>
durch
<parallel>false</parallel>
und führen Sie die Tests erneut aus: Während bei ersterer paralleler Testausführung als
Gesamttestzeit ("Time elapsed") ein Wert knap über 2 Sekunden gemeldet wurde, wird
diesmal mit serieller Ausführung ein Wert über 10 Sekunden gemeldet, also fast das
fünffache.
89. Wenn Sie TestNG-Tests in Eclipse ausführen wollen, müssen Sie das TestNG-EclipsePlug-in installieren, siehe http://testng.org/doc/eclipse.html und http://beust.com/eclipse/.
JMockit
JMockit ist ein Mocking-Framework, welches während Testausführungen Methoden anderer
Klassen simuliert und ersetzt und bestimmte Ergebnisse vortäuscht. Das Besondere an JMockit
ist, dass es auch Konstruktoren sowie statische und finale Methoden mocken kann.
Alternativen zu JMockit sind Mockito und EasyMock sowie die Erweiterung PowerMock.
1. Wechseln Sie in Ihr Projekte-Verzeichnis und erzeugen Sie ein neues Projekt:
cd \MeinWorkspace
md MvnJMockit
cd MvnJMockit
md src\main\java\de\meinefirma\meinprojekt
md src\test\java\de\meinefirma\meinprojekt
2. Erzeugen Sie im Projektverzeichnis die Projektkonfigurationsdatei: pom.xml
3.
4. <project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
5.
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
6.
<modelVersion>4.0.0</modelVersion>
7.
<groupId>de.meinefirma.meinprojekt</groupId>
8.
<artifactId>MvnJMockit</artifactId>
9.
<version>1.0-SNAPSHOT</version>
10.
<dependencies>
11.
<dependency>
12.
<groupId>com.googlecode.jmockit</groupId>
13.
<artifactId>jmockit</artifactId>
14.
<version>1.7</version>
15.
</dependency>
16.
<dependency>
17.
<groupId>junit</groupId>
18.
<artifactId>junit</artifactId>
19.
<version>4.12</version>
20.
<scope>test</scope>
21.
</dependency>
22.
</dependencies>
23. </project>
Beachten Sie, dass jmockit vor junit in den dependencies eingetragen sein muss.
24. Erzeugen Sie im src\main\java\de\meinefirma\meinprojekt-Verzeichnis die zu
testende Klasse: Mittagspausenzeit.java
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
package de.meinefirma.meinprojekt;
import java.time.LocalDateTime;
public class Mittagspausenzeit
{
public static boolean isMittagspause()
{
int stunde = LocalDateTime.now().getHour();
return stunde >= 12 && stunde <= 13;
}
}
Erzeugen Sie im src\test\java\de\meinefirma\meinprojekt-Verzeichnis
Testklasse: MittagspausenzeitTest.java
die
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
package de.meinefirma.meinprojekt;
import java.time.LocalDateTime;
import mockit.*;
import org.junit.*;
public class MittagspausenzeitTest
{
@Test
public void test12uhr( @Mocked final LocalDateTime dateTime )
{
new Expectations() {{
LocalDateTime.now(); result = dateTime;
dateTime.getHour(); result = Integer.valueOf( 12 );
}};
boolean isMittagspause = Mittagspausenzeit.isMittagspause();
Assert.assertTrue( isMittagspause );
}
}
Damit der Test zu jeder Tageszeit ausgeführt werden kann, wird als Ergebnis von
LocalDateTime.now().getHour() 12 Uhr vorgetäuscht.
Sehen Sie sich das JMockit Testing Toolkit Tutorial und die JMockit Toolkit API Doku
an, sowie @Mocked und Expectations.
60. Ihr Projekteverzeichnis sieht jetzt so aus:
61.
62. [\MeinWorkspace\MvnJMockit]
63.
|- [src]
64.
|
|- [main]
65.
|
|
'- [java]
66.
|
|
'- [de]
67.
|
|
'- [meinefirma]
68.
|
|
'- [meinprojekt]
69.
|
|
'- Mittagspausenzeit.java
70.
|
'- [test]
71.
|
'- [java]
72.
|
'- [de]
73.
|
'- [meinefirma]
74.
|
'- [meinprojekt]
75.
|
'- MittagspausenzeitTest.java
76.
'- pom.xml
77. Führen Sie die den Test aus:
cd \MeinWorkspace\MvnJMockit
mvn test
Automatisierter Integrationstest mit Jetty, HtmlUnit und HttpUnit
Das folgende Beispiel enthält zwei verschiedene Testarten, die beide automatisiert ausgeführt
werden können:


Einen einfachen JUnit-Komponententest (der ohne Servlet-Engine funktioniert)
Mit HtmlUnit und HttpUnit realisierte Integrationstests, die nur mit laufender ServletEngine (z.B. Jetty) funktionieren
Erläuterungen zu den verschiedenen Testarten "Komponententest" und "Integrationstest" finden
Sie unter java-fit.htm#Testebenen.
Zum besseren Verständnis und um es einfach zu halten sind in diesem Beispiel
Komponententests und Integrationstests in einem einzigen Maven-Projekt vereint. In realen
Projekten kann es manchmal sinnvoll sein, die Integrationstests in einem getrennten MavenProjekt zu realisieren.
Die Verwendung von sowohl HtmlUnit als auch HttpUnit zusammen in einem Projekt ist
normalerweise nicht sinnvoll, da beide Testframeworks ähnliche Ziele abdecken. Insbesondere
im folgenden Beispiel ist der einzige Sinn, beides demonstrieren zu können.
1. Öffnen Sie ein Kommandozeilenfenster, wechseln Sie in Ihr Projekte-Verzeichnis (z.B.
\MeinWorkspace) und erzeugen Sie eine Webapp-Projektstruktur:
cd \MeinWorkspace
mvn archetype:generate -DinteractiveMode=false DarchetypeArtifactId=maven-archetype-webapp DgroupId=de.meinefirma.meinprojekt -DartifactId=MvnHtmlIntTest
cd MvnHtmlIntTest
md src\main\java\de\meinefirma\meinprojekt
md src\test\java\de\meinefirma\meinprojekt
md src\test\java\integrationtest
rd src\main\resources
tree /F
Sie erhalten:
[\MeinWorkspace\MvnHtmlIntTest]
|- [src]
|
|- [main]
|
|
|- [java]
|
|
|
'- [de]
|
|
|
'- [meinefirma]
|
|
|
'- [meinprojekt]
|
|
'- [webapp]
|
|
|- [WEB-INF]
|
|
|
'- web.xml
|
|
'- index.jsp
|
'- [test]
|
'- [java]
|
|- [de]
|
|
'- [meinefirma]
|
|
'- [meinprojekt]
|
'- [integrationtest]
'- pom.xml
2. Ersetzen Sie im Projektverzeichnis den Inhalt der pom.xml durch:
3.
4. <project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
5.
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
6.
<modelVersion>4.0.0</modelVersion>
7.
<groupId>de.meinefirma.meinprojekt</groupId>
8.
<artifactId>MvnHtmlIntTest</artifactId>
9.
<version>1.0-SNAPSHOT</version>
10.
<packaging>war</packaging>
11.
<name>MvnHtmlIntTest: Webapp mit Integrationstest</name>
12.
<build>
13.
<finalName>${project.artifactId}</finalName>
14.
<plugins>
15.
<plugin>
16.
<!-- Die Versionen 8.1.16.v20140903 und 9.2.2.v20140723
funktionieren,
17.
aber 9.2.3.v20140905 und 9.3.10.v20160621 funktionieren
nicht: -->
18.
<groupId>org.eclipse.jetty</groupId>
19.
<artifactId>jetty-maven-plugin</artifactId>
20.
<version>9.2.2.v20140723</version>
21.
<configuration>
22.
<scanIntervalSeconds>10</scanIntervalSeconds>
23.
<stopPort>9999</stopPort>
24.
<stopKey>geheim</stopKey>
25.
<stopWait>1</stopWait>
26.
<webAppConfig>
27.
<contextPath>/${project.artifactId}</contextPath>
28.
</webAppConfig>
29.
</configuration>
30.
<executions>
31.
<execution>
32.
<id>start-jetty</id>
33.
<phase>pre-integration-test</phase>
34.
<goals>
35.
<goal>run</goal>
36.
</goals>
37.
<configuration>
38.
<scanIntervalSeconds>0</scanIntervalSeconds>
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
<daemon>true</daemon>
</configuration>
</execution>
<execution>
<id>stop-jetty</id>
<phase>post-integration-test</phase>
<goals>
<goal>stop</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.20</version>
<configuration>
<systemPropertyVariables>
<port>8080</port>
</systemPropertyVariables>
<excludes>
<exclude>**/integrationtest/*Test.java</exclude>
</excludes>
</configuration>
<executions>
<execution>
<id>integration-tests</id>
<phase>integration-test</phase>
<goals>
<goal>test</goal>
</goals>
<configuration>
<skip>false</skip>
<excludes>
<exclude>none</exclude>
</excludes>
<includes>
<include>**/integrationtest/*Test.java</include>
</includes>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>net.sourceforge.htmlunit</groupId>
<artifactId>htmlunit</artifactId>
<version>2.20</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.httpunit</groupId>
<artifactId>httpunit</artifactId>
<version>1.7.2</version>
<scope>test</scope>
96.
</dependency>
97.
<dependency>
98.
<groupId>junit</groupId>
99.
<artifactId>junit</artifactId>
100.
<version>4.12</version>
101.
<scope>test</scope>
102.
</dependency>
103.
</dependencies>
104. </project>
Erläuterungen zu den Konfigurationsoptionen des Jetty-Plugins finden Sie unter
http://eclipse.org/jetty/documentation/current/jetty-maven-plugin.html (sehen Sie sich
insbesondere scanIntervalSeconds und daemon an).
Erläuterungen zu den Optionen des Surefire-Test-Plugins finden Sie unter
http://maven.apache.org/plugins/maven-surefire-plugin/.
Ersetzen Sie im src\main\webapp-Verzeichnis den Inhalt der index.jsp durch:
106.
107. <html>
108. <head><title>Startseite</title></head>
109. <body>
110. <h2>Startseite</h2>
111. <p><a name="BerechnungsFormular"
href="BerechnungsFormular.jsp">Berechnungsformular</a></p>
112. </body>
113. </html>
114.
Fügen Sie im src\main\webapp-Verzeichnis folgende
BerechnungsFormular.jsp hinzu:
115.
116. <%@ page import="de.meinefirma.meinprojekt.MeineBean" %>
117. <html>
118. <head><title>Webseite mit Berechnungsformular</title></head>
119. <body>
120. <h2>Webseite mit Berechnungsformular</h2>
121. <% String wert1 = request.getParameter( "wert1" );
122.
String wert2 = request.getParameter( "wert2" );
123.
if( wert1 == null ) { wert1 = ""; }
124.
if( wert2 == null ) { wert2 = ""; }
125.
String ergebnis = (new MeineBean()).berechne( wert1, wert2 ); %>
126. <form name="meinFormular" method="post"><pre>
127. Wert1 <input type="text" name="wert1" value='<%= wert1 %>' size=10
maxlength=10><br>
128. Wert2 <input type="text" name="wert2" value='<%= wert2 %>' size=10
maxlength=10><br>
129.
<input type="submit" name="berechne" value="berechne"> <input
type="text" name="ergebnis" value='<%= ergebnis %>' size=10
readonly><br>
130. </pre></form>
131. </body>
132. </html>
133.
Erstellen Sie im src\main\java\de\meinefirma\meinprojekt-Verzeichnis
eine JavaBean: MeineBean.java
134.
105.
135. package de.meinefirma.meinprojekt;
136.
137. public class MeineBean
138. {
139.
public String berechne( String d1, String d2 )
140.
{
141.
try {
142.
return "" + (Double.parseDouble( d1 ) + Double.parseDouble(
d2 ));
143.
} catch( Exception ex ) {
144.
return "";
145.
}
146.
}
147. }
148.
Erstellen Sie im src\test\java\de\meinefirma\meinprojekt-Verzeichnis
einen Komponenten-JUnit-Test: MeineBeanTest.java
149.
150. package de.meinefirma.meinprojekt;
151.
152. import org.junit.*;
153.
154. public class MeineBeanTest
155. {
156.
@Test
157.
public void testMeineBean()
158.
{
159.
MeineBean meineBean = new MeineBean();
160.
Assert.assertEquals( "3.0", meineBean.berechne( "1", "2" ) );
161.
}
162. }
163.
Erstellen Sie im src\test\java\integrationtest-Verzeichnis einen
HtmlUnit-Integrationstest: HtmlUnitTest.java
164.
165. package integrationtest;
166.
167. import org.junit.*;
168. import com.gargoylesoftware.htmlunit.WebClient;
169. import com.gargoylesoftware.htmlunit.html.*;
170.
171. public class HtmlUnitTest
172. {
173.
@Test
174.
public void test() throws Exception
175.
{
176.
String port = System.getProperty( "port" );
177.
test( port, "1", "2", "3.0" );
178.
}
179.
180.
public String test( String port, String wert1, String wert2, String
ergebnis ) throws Exception
181.
{
182.
WebClient webClient = new WebClient();
183.
HtmlPage page = (HtmlPage) webClient.getPage(
"http://localhost:" + port + "/MvnHtmlIntTest/" );
184.
Assert.assertEquals( "Titel: ", "Startseite",
page.getTitleText() );
185.
page = (HtmlPage) page.getAnchorByName( "BerechnungsFormular"
).click();
186.
Assert.assertEquals( "Titel: ", "Webseite mit
Berechnungsformular", page.getTitleText() );
187.
HtmlForm form = page.getFormByName( "meinFormular" );
188.
form.getInputByName( "wert1" ).setValueAttribute( wert1 );
189.
form.getInputByName( "wert2" ).setValueAttribute( wert2 );
190.
page = (HtmlPage) form.getInputByName( "berechne" ).click();
191.
String e = page.getFormByName( "meinFormular" ).getInputByName(
"ergebnis" ).getValueAttribute();
192.
if( ergebnis != null ) { Assert.assertEquals( "Ergebnis: ",
ergebnis, e ); }
193.
return e;
194.
}
195. }
196.
Erstellen Sie im src\test\java\integrationtest-Verzeichnis einen HttpUnitIntegrationstest: HttpUnitTest.java
197.
198. package integrationtest;
199.
200. import org.junit.*;
201. import com.meterware.httpunit.*;
202.
203. public class HttpUnitTest
204. {
205.
@Test
206.
public void test() throws Exception
207.
{
208.
String port = System.getProperty( "port" );
209.
test( port, "1", "2", "3.0" );
210.
}
211.
212.
public String test( String port, String wert1, String wert2, String
ergebnis ) throws Exception
213.
{
214.
WebConversation conversation = new WebConversation();
215.
WebRequest
request = new GetMethodWebRequest(
"http://localhost:" + port + "/MvnHtmlIntTest/" );
216.
WebResponse
response = conversation.getResponse( request );
217.
Assert.assertEquals( "ReturnCode: ", 200,
response.getResponseCode() );
218.
Assert.assertEquals( "ReturnMsg: ", "OK",
response.getResponseMessage() );
219.
Assert.assertEquals( "Titel: ", "Startseite",
response.getTitle() );
220.
response = response.getLinkWithName( "BerechnungsFormular"
).click();
221.
Assert.assertEquals( "Titel: ", "Webseite mit
Berechnungsformular", response.getTitle() );
222.
WebForm form = response.getFormWithName( "meinFormular" );
223.
form.setParameter( "wert1", wert1 );
224.
form.setParameter( "wert2", wert2 );
225.
form.getSubmitButton( "berechne" ).click();
226.
response = conversation.getCurrentPage();
227.
form = response.getFormWithName( "meinFormular" );
228.
String e = form.getParameterValue( "ergebnis" );
229.
if( ergebnis != null ) { Assert.assertEquals( "Ergebnis: ",
ergebnis, e ); }
230.
return e;
231.
}
232. }
Die Abfrage if( ergebnis != null ) erscheint im Moment noch etwas unsinnig: Sie
wird erst später in MeinFixture im HTML-Akzeptanztest mit Fit benötigt.
233.
234.
235.
236.
237.
238.
239.
240.
241.
242.
243.
244.
245.
246.
247.
248.
249.
250.
251.
252.
253.
254.
255.
256.
257.
258.
Die Projektstruktur sieht jetzt so aus (überprüfen Sie es mit tree /F):
[\MeinWorkspace\MvnHtmlIntTest]
|- [src]
|
|- [main]
|
|
|- [java]
|
|
|
'- [de]
|
|
|
'- [meinefirma]
|
|
|
'- [meinprojekt]
|
|
|
'- MeineBean.java
|
|
'- [webapp]
|
|
|- [WEB-INF]
|
|
|
'- web.xml
|
|
|- BerechnungsFormular.jsp
|
|
'- index.jsp
|
'- [test]
|
'- [java]
|
|- [de]
|
|
'- [meinefirma]
|
|
'- [meinprojekt]
|
|
'- MeineBeanTest.java
|
'- [integrationtest]
|
|- HtmlUnitTest.java
|
'- HttpUnitTest.java
'- pom.xml
Manueller Test im Webbrowser:
cd \MeinWorkspace\MvnHtmlIntTest
mvn jetty:run
Warten Sie bis "Started Jetty Server" erscheint und rufen Sie dann im Webbrowser
auf:
http://localhost:8080/MvnHtmlIntTest
Klicken Sie auf den Berechnungsformular-Link, tragen Sie auf der folgenden Webseite
zwei Zahlen ein und betätigen Sie "berechne": Sie erhalten die Summe.
Beenden Sie Jetty mit "Strg + C".
259.
Testen verschiedener Maven-Kommandos:
Normaler Build (MvnHtmlIntTest.war wird im target-Verzeichnis erzeugt):
mvn clean package
dir target\*.war
Nur einen einzelnen bestimmten Komponententest ausführen:
mvn test -Dtest=de.meinefirma.meinprojekt.MeineBeanTest
Build ohne Tests:
mvn package -Dmaven.test.skip=true
260.
Manueller Aufruf einzelner bestimmter Integrationstests:
Versuchen Sie zuerst testweise einen Integrationstest ohne laufenden Jetty aufzurufen
und sehen Sie sich die Fehlermeldung in target\surefirereports\integrationtest.HtmlUnitTest.txt an:
mvn test -Dtest=integrationtest.HtmlUnitTest
type target\surefire-reports\integrationtest.HtmlUnitTest.txt
Starten Sie jetzt in einem Kommandozeilenfenster Jetty mit der Webanwendung:
mvn jetty:run
Starten Sie in einem zweiten Kommandozeilenfenster die Integrationstests (diesmal ohne
Fehlermeldung):
cd \MeinWorkspace\MvnHtmlIntTest
mvn test -Dtest=integrationtest.HtmlUnitTest
mvn test -Dtest=integrationtest.HttpUnitTest
mvn test -Dtest=integrationtest.*Test
Beenden Sie Jetty mit "Strg + C".
261.
Automatisierter Integrationstest:
Der wichtigste Trick in diesem Beispiel ist die vollständig automatisierte Ausführung der
Integrationstests inklusive automatischem Start des Jetty-Servers in einem zusätzlichen
Hintergrund-Thread (über <daemon>true</daemon>), Ausführung der Integrationstests
und anschließend der automatischen Beendigung von Jetty.
Sie sollten normalerweise nicht "mvn integration-test", sondern stattdessen "mvn
verify" verwenden, damit auch die "post-integration-test"-Phase ausgeführt wird:
mvn clean verify
Sie erhalten zwei Testphasen, zuerst default-test und dann integration-tests:
...
[INFO] --- maven-surefire-plugin:2.20:test (default-test) @
MvnHtmlIntTest --...
------------------------------------------------------T E S T S
------------------------------------------------------Running de.meinefirma.meinprojekt.MeineBeanTest
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
...
[INFO] --- jetty-maven-plugin:9.2.2.v20140723:run (start-jetty) @
MvnHtmlIntTest --...
[INFO] Started Jetty Server
[INFO]
[INFO] --- maven-surefire-plugin:2.20:test (integration-tests) @
MvnHtmlIntTest --...
------------------------------------------------------T E S T S
------------------------------------------------------Running integrationtest.HtmlUnitTest
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
Running integrationtest.HttpUnitTest
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
Results :
Tests run: 2, Failures: 0, Errors: 0, Skipped: 0
...
[INFO] --- jetty-maven-plugin:9.2.2.v20140723:stop (stop-jetty) @
MvnHtmlIntTest --...
[INFO] Server reports itself as stopped
[INFO] ----------------------------------------------------------------------[INFO] BUILD SUCCESS
[INFO] -----------------------------------------------------------------------
262.
Dependencies:
Lassen Sie sich folgendermaßen einen Dependency-Baum anzeigen und sehen Sie sich
an, wie Maven automatisch Versionskonflikte bei den Abhängigkeiten gelöst hat:
mvn dependency:tree -Dverbose
263.
Eclipse:
Um Maven-Projekte in Eclipse bearbeiten zu können, müssen Sie verfahren wie oben
unter Maven mit Eclipse beschrieben ist.
Ein besonderer Vorteil des Jetty-Plugins ist, dass Sie über das scanIntervalSecondsAttribut steuern können, ob Änderungen direkt in den Webcontainer übernommen
werden, ohne dass ein Redeployment notwendig ist. Testen Sie dies, indem Sie zuerst
Jetty über "mvn jetty:run" starten und anschließend in Eclipse Änderungen zum
Beispiel an BerechnungsFormular.jsp, MeineBean.java oder anderen Dateien
durchführen.
264.
Infos zu HttpUnit und HtmlUnit:
Ein weiteres Programmierbeispiel zu HttpUnit finden Sie unter java-httpunit.htm.
Weiteres zu HttpUnit finden Sie unter http://httpunit.sourceforge.net und
http://httpunit.sourceforge.net/doc/api/.
Weiteres zu HtmlUnit finden Sie unter http://htmlunit.sourceforge.net und
http://htmlunit.sourceforge.net/apidocs/.
265.
Erweiterungen:
Wie Sie Ihre Integrationstests mit Fit und Cargo erweitern, erfahren Sie weiter unten.
Um die Datenbank mit bestimmten Inhalten vorzubereiten, können Sie das DbUnit
Maven Plugin verwenden.
Wenn Sie erweiterte Möglichkeiten für Integrationstests benötigen, sollten Sie sich das
Maven Failsafe Plugin ansehen. Siehe hierzu auch den Sonatype-Blog-Eintrag Part 1 und
zur Testabdeckung in Integrationstests mit emma4it den Part 2.
Automatisierter Integrationstest mit Jetty und JWebUnit
JWebUnit leistet Ähnliches wie HtmlUnit und HttpUnit, aber ist oft einfacher und
übersichtlicher.
1. Voraussetzung für das folgende Beispiel ist die Durchführung des vorherigen Beispiels
Automatisierter Integrationstest mit Jetty, HtmlUnit und HttpUnit (im Folgenden
MvnHtmlIntTest genannt).
2. Erzeugen Sie ein neues Projektverzeichnis MvnJWebUnitIntTest und kopieren Sie
dorthin das MvnHtmlIntTest-Projekt:
cd \MeinWorkspace
md MvnJWebUnitIntTest
cd MvnJWebUnitIntTest
xcopy ..\MvnHtmlIntTest\*.* /S
3. Entfernen Sie in der pom.xml im MvnJWebUnitIntTest-Projektverzeichnis die beiden
<dependency>...-Blöcke zu htmlunit und httpunit und fügen Sie stattdessen
folgenden neuen <dependency>...-Block ein:
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
<dependency>
<groupId>net.sourceforge.jwebunit</groupId>
<artifactId>jwebunit-htmlunit-plugin</artifactId>
<version>3.3</version>
<scope>test</scope>
</dependency>
Löschen Sie im src\test\java\integrationtest-Verzeichnis die beiden Dateien
HtmlUnitTest.java und HttpUnitTest.java und erzeugen Sie stattdessen im selben
Verzeichnis die neue Testklasse JWebUnitTest.java:
package integrationtest;
import net.sourceforge.jwebunit.junit.WebTestCase;
public class JWebUnitTest extends WebTestCase
{
public void test()
{
setBaseUrl(
"http://localhost:8080/MvnHtmlIntTest/" );
beginAt(
"/index.jsp" );
assertTitleEquals( "Startseite" );
clickLinkWithText( "Berechnungsformular" );
assertTitleEquals( "Webseite mit Berechnungsformular" );
setTextField(
"wert1", "1" );
setTextField(
"wert2", "2" );
submit();
assertTextFieldEquals( "ergebnis", "3.0" );
}
}
Falls Sie die JWebUnit-XPath-Methoden probieren wollen: Den Ergebniswert können
Sie beispielsweise abfragen mit:
getElementAttributeByXPath( "//input[lower-case(@type)='text' and
@name='ergebnis']", "value" );
32. Die Projektstruktur sieht jetzt so aus (überprüfen Sie es mit tree /F):
33.
34. [\MeinWorkspace\MvnJWebUnitIntTest]
35.
|- [src]
36.
|
|- [main]
37.
|
|
|- [java]
38.
|
|
|
'- [de]
39.
|
|
|
'- [meinefirma]
40.
|
|
|
'- [meinprojekt]
41.
|
|
|
'- MeineBean.java
42.
|
|
'- [webapp]
43.
|
|
|- [WEB-INF]
44.
|
|
|
'- web.xml
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
|
|
|- BerechnungsFormular.jsp
|
|
'- index.jsp
|
'- [test]
|
'- [java]
|
|- [de]
|
|
'- [meinefirma]
|
|
'- [meinprojekt]
|
|
'- MeineBeanTest.java
|
'- [integrationtest]
|
'- JWebUnitTest.java
'- pom.xml
56. Führen Sie den Integrationstest aus (wieder mit Jetty im Hintergrund-Thread):
cd \MeinWorkspace\MvnJWebUnitIntTest
mvn clean verify
57. Weiteres zu JWebUnit finden Sie unter http://jwebunit.sourceforge.net,
http://jwebunit.sourceforge.net/apidocs/ und
http://jwebunit.sourceforge.net/apidocs/net/sourceforge/jwebunit/junit/WebTestCase.html
.
Automatisierter Integrationstest mit Selenium
Mit Selenium können Sie über automatisierte Integrationstests Webanwendungen über einen
Webbrowser testen. Dies hat den Vorteil, dass JavaScript und Ajax leichter und realitätsnäher
getestet werden. Allerdings gibt es auch einen Nachteil: Da die Installation eines Webbrowsers
vorausgesetzt wird (außer bei Benutzung des HtmlUnitDriver), ist die Ausführung auf
beliebigen PCs und Betriebssystemen nicht mehr so einfach zu gewährleisten und die
Ausführung in Continuous-Integration-Systemen könnte unsicherer sein. Falls Sie nur einfache
HTML-Anwendungen ohne DHTML, JavaScript und Ajax testen wollen, können HtmlUnit und
HttpUnit besser geeignet sein.
Bitte beachten Sie die drei grundsätzlich verschiedenen Selenium-Versionen:

Selenium 1, auch Selenium RC (Remote Control) genannt:
Selenium 1 (RC) ist der Vorgänger von Selenium 2 (WebDriver) und sollte möglichst
nicht mehr für neue Anwendungen verwendet werden. Selenium 1 (RC) basiert auf der
Verwendung eines für den Test gestarteten Proxy-Servers (Selenium-Server), welcher
den gewünschten Webbrowser bedient. Die Testprogrammierung beginnt typischerweise
mit zwei Zeilen ähnlich zu:
Selenium selenium = new DefaultSelenium( "localhost", 4444, "*firefox",
"http://www.meintesthost.de" );
selenium.start();
Siehe: Selenium 1 (Selenium RC) und Migrating From Selenium RC to Selenium
WebDriver.

Selenium 2, auch Selenium WebDriver genannt:
Anders als Selenium 1 verwendet Selenium 2 (WebDriver) keinen Proxy-Server, sondern
spricht den gewünschten Webbrowser direkt an, über browserspezifische WebDriver.
Beispielsweise für den Firefox-Webbrowser könnte das Testprogramm mit einer Zeile
beginnen ähnlich zu:
WebDriver webDriver = new FirefoxDriver();
Siehe: Selenium WebDriver und Selenium-Doku.
Das unten gezeigte Beispiel demonstriert, wie zur Laufzeit der gewünschten Webbrowser
gewählt werden kann.
Selenium 2 (WebDriver) bietet ein neues und verbessertes Programmier-API. Für
Migrationszwecke kann aber auch in Selenium 2 auf viele Funktionen des alten
Selenium-1-Programmier-APIs zurückgegriffen werden, indem folgendermaßen das alte
Selenium-Objekt erzeugt wird:
Selenium selenium = new WebDriverBackedSelenium( webDriver,
"http://www.meintesthost.de" );

Selenium IDE:
Selenium IDE ist eine Erweiterung (Plug-in) für den Firefox-Webbrowser, womit schnell
und einfach im Firefox-Webbrowser ablauffähige Skripte zu Benutzerinteraktionen
erstellt und abgespielt werden können (record-and-playback), beispielsweise zum
Reproduzieren von Fehlersituationen oder zur Vorbereitung von automatisierten Tests
(Prototyping). Die mit Selenium IDE generierbaren Skripte sind nicht für Integrationsund Regressionstests vorgesehen, wo hohe Stabilität und Reproduzierbarkeit wichtig ist.
Siehe: Selenium IDE Übersicht und Selenium IDE Doku.
Das folgende Beispiel verwendet ausschließlich Selenium 2 (WebDriver).
1. Sehen Sie sich die Selenium-Doku, das Interface WebDriver und die Packages
org.openqa.selenium und org.openqa.selenium.support.ui an.
2. Legen Sie ein neues Projekt an:
cd \MeinWorkspace
md MvnSelenium
cd MvnSelenium
md src\test\java\integrationtest
3. Erzeugen Sie im Projektverzeichnis die Projektkonfiguration: pom.xml
4.
5. <project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
6.
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
7.
<modelVersion>4.0.0</modelVersion>
8.
<groupId>de.meinefirma.meinprojekt</groupId>
9.
<artifactId>MvnSelenium</artifactId>
10.
<version>1.0-SNAPSHOT</version>
11.
<packaging>jar</packaging>
12.
<name>MvnSelenium</name>
13.
<build>
14.
<plugins>
15.
<plugin>
16.
<groupId>org.apache.maven.plugins</groupId>
17.
<artifactId>maven-compiler-plugin</artifactId>
18.
<version>3.6.1</version>
19.
<configuration>
20.
<source>1.7</source>
21.
<target>1.7</target>
22.
</configuration>
23.
</plugin>
24.
</plugins>
25.
</build>
26.
<dependencies>
27.
<dependency>
28.
<groupId>org.seleniumhq.selenium</groupId>
29.
<artifactId>selenium-java</artifactId>
30.
<version>3.4.0</version>
31.
<scope>test</scope>
32.
</dependency>
33.
<dependency>
34.
<groupId>org.seleniumhq.selenium</groupId>
35.
<artifactId>selenium-htmlunit-driver</artifactId>
36.
<version>2.52.0</version>
37.
<scope>test</scope>
38.
</dependency>
39.
<dependency>
40.
<groupId>junit</groupId>
41.
<artifactId>junit</artifactId>
42.
<version>4.12</version>
43.
<scope>test</scope>
44.
</dependency>
45.
</dependencies>
46. </project>
47.
48.
Erzeugen Sie im src\test\java\integrationtest-Verzeichnis den
WebDriver-Wrapper: WebDriverWrapper.java
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
package integrationtest;
import
import
import
import
import
import
import
org.openqa.selenium.*;
org.openqa.selenium.chrome.ChromeDriver;
org.openqa.selenium.edge.EdgeDriver;
org.openqa.selenium.firefox.FirefoxDriver;
org.openqa.selenium.htmlunit.HtmlUnitDriver;
org.openqa.selenium.ie.InternetExplorerDriver;
org.openqa.selenium.safari.SafariDriver;
/**
* Wrapper fuer Selenium-WebDriver fuer waehlbaren Webbrowser und als
AutoCloseable
63.
*/
64. public class WebDriverWrapper implements AutoCloseable
65. {
66.
private WebDriver webDriver;
67.
68.
/**
69.
* Selenium-WebDriver fuer waehlbaren Webbrowser instanziieren.
70.
* @param browserName
leer fuer HtmlUnitDriver oder
71.
*
FF, Chrome, Edge, IE oder Safari fuer
gewuenschten Webbrowser.
72.
* @param webDriverExePath leer fuer HtmlUnitDriver, FF und Safari;
73.
*
fuer Chrome und IE wird Pfad zum
externen WebDriver.exe benoetigt,
74.
*
z.B. zu chromedriver.exe,
iexploredriver.exe bzw. MicrosoftWebDriver.exe.
75.
*/
76.
public WebDriverWrapper( String browserName, String
webDriverExePath )
77.
{
78.
if( browserName != null ) {
79.
String brwsr = browserName.trim().toLowerCase();
80.
String wbdrv = ( webDriverExePath != null ) ?
webDriverExePath.trim() : null;
81.
if( brwsr.contains( "firefox" ) || brwsr.contains( "ff" ) ) {
82.
webDriver = new FirefoxDriver();
83.
} else if( brwsr.contains( "chrome" ) ) {
84.
if( wbdrv != null ) { System.setProperty(
"webdriver.chrome.driver", wbdrv ); }
85.
webDriver = new ChromeDriver();
86.
} else if( brwsr.contains( "edge" ) ) {
87.
if( wbdrv == null ) {
88.
wbdrv = "C:\\Program Files (x86)\\Microsoft Web
Driver\\MicrosoftWebDriver.exe";
89.
}
90.
System.setProperty( "webdriver.edge.driver", wbdrv );
91.
webDriver = new EdgeDriver();
92.
} else if( brwsr.contains( "internetexplorer" ) ||
brwsr.contains( "ie" ) ) {
93.
if( wbdrv != null ) { System.setProperty(
"webdriver.ie.driver", wbdrv ); }
94.
webDriver = new InternetExplorerDriver();
95.
} else if( brwsr.contains( "safari" ) ) {
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
106.
107.
108.
109.
110.
111.
112.
113.
114.
115. }
116.
webDriver = new SafariDriver();
}
}
if( webDriver == null ) {
webDriver = new HtmlUnitDriver();
}
}
public WebDriver getWebDriver()
{
return webDriver;
}
@Override
public void close()
{
webDriver.quit();
webDriver = null;
}
117.
118. Erzeugen Sie im src\test\java\integrationtest-Verzeichnis die
Testklasse: SeleniumSimpleTest.java
119.
120.
121. package integrationtest;
122.
123. import org.junit.*;
124. import org.openqa.selenium.*;
125. import org.openqa.selenium.support.ui.*;
126.
127. /**
128. * Einfacher Selenium-Test mit der Wikipedia-Webseite.<br>
129. * Wahlweise ausfuehrbar mit manueller main()-Methode
130. * oder automatisiert als JUnit-Test.
131. */
132. public class SeleniumSimpleTest
133. {
134.
public static final String BROWSERNAME_KEY
= "_BROWSERNAME";
135.
public static final String WEBDRIVEREXEPATH_KEY =
"_WEBDRIVEREXEPATH";
136.
137.
/**
138.
* Manuell ausfuehrbarer Selenium-Test.<br>
139.
* @param args 1. Parameter leer fuer HtmlUnitDriver oder
140.
*
FF, Chrome, Edge, IE oder Safari fuer
gewuenschten Webbrowser.<br>
141.
*
2. Parameter leer fuer HtmlUnitDriver, FF und
Safari;
142.
*
fuer Chrome und IE wird Pfad zum externen
WebDriver.exe benoetigt,
143.
*
z.B. zu chromedriver.exe, iexploredriver.exe
bzw. MicrosoftWebDriver.exe.<br>
144.
*/
145.
public static void main( String[] args )
146.
{
147.
String browserName = null, webDriverExePath = null;
148.
if( args != null ) {
149.
if( args.length > 0 ) { browserName
= args[0]; }
150.
if( args.length > 1 ) { webDriverExePath = args[1]; }
151.
}
152.
testSelenium( browserName, webDriverExePath );
153.
}
154.
155.
/**
156.
* Automatisiert ausfuehrbarer JUnit-Selenium-Test.<br>
157.
* Falls die Umgebungsvariablen _BROWSERNAME und _WEBDRIVEREXEPATH
158.
* gesetzt sind, kann darueber der gewuenschte Webbrowser definiert
werden.<br>
159.
* _BROWSERNAME:
leer fuer HtmlUnitDriver oder
160.
*
FF, Chrome, Edge, IE oder Safari fuer
gewuenschten Webbrowser.<br>
161.
* _WEBDRIVEREXEPATH: leer fuer HtmlUnitDriver, FF und Safari;
162.
*
fuer Chrome und IE wird Pfad zum externen
WebDriver.exe benoetigt,
163.
*
z.B. zu chromedriver.exe, iexploredriver.exe
bzw. MicrosoftWebDriver.exe.<br>
164.
*/
165.
@Test
166.
public void testSelenium()
167.
{
168.
String browserName
= System.getProperty( BROWSERNAME_KEY );
169.
String webDriverExePath = System.getProperty(
WEBDRIVEREXEPATH_KEY );
170.
Assert.assertTrue( testSelenium( browserName, webDriverExePath )
);
171.
}
172.
173.
private static boolean testSelenium( String browserName, String
webDriverExePath )
174.
{
175.
final String url = "http://de.wikipedia.org";
176.
final String ttl = "Selenium \u2013 Wikipedia";
177.
178.
// WebDriver starten:
179.
try( WebDriverWrapper wdwrap = new WebDriverWrapper(
browserName, webDriverExePath ) ) {
180.
WebDriver webDriver = wdwrap.getWebDriver();
181.
WebDriverWait wait = new WebDriverWait( webDriver, 10 );
182.
183.
// Webseite laden, Daten eintragen und Antwort anfordern:
184.
webDriver.get( url );
185.
WebElement element = wait.until(
ExpectedConditions.presenceOfElementLocated( By.id( "searchInput" ) )
);
186.
String titelAbfrageseite = webDriver.getTitle();
187.
element.sendKeys( "Selenium" );
188.
element = webDriver.findElement( By.name( "go" ) );
189.
element.click();
190.
191.
// Ergebnisse pruefen:
192.
boolean ok = wait.until( ExpectedConditions.titleIs( ttl )
).booleanValue();
193.
String titelErgebnisseite = webDriver.getTitle();
194.
String hauptueberschrift = webDriver.findElement( By.id(
"firstHeading" ) ).getText();
195.
ok &= ttl.equals( titelErgebnisseite ) && "Selenium".equals(
hauptueberschrift );
196.
System.out.println( "\n-----------------------------------------------------------" );
197.
if( browserName != null ) {
198.
System.out.println( "Webbrowsername:
" +
browserName );
199.
}
200.
System.out.println( "WebDriver:
" +
webDriver.getClass().getSimpleName() );
201.
System.out.println( "Test-URL:
" + url );
202.
System.out.println( "Titel der Abfrage-Seite: " +
titelAbfrageseite );
203.
System.out.println( "Titel der Ergebnis-Seite: " +
titelErgebnisseite );
204.
System.out.println( "Hauptueberschrift:
" +
hauptueberschrift );
205.
System.out.println( ( ok ) ? "OK" : "Fehler" );
206.
System.out.println( "-----------------------------------------------------------\n" );
207.
return ok;
208.
}
209.
}
210. }
211.
212. Der Test startet einen Selenium-WebDriver im WebDriverWrapper,
213.
öffnet die Wikipedia-Startseite in webDriver.get( url ),
214.
sucht das Input-Textfeld "searchInput",
215.
trägt den Suchbegriff "Selenium" ein
und überprüft die Ergebnis-Webseite.
216.
217. Die Projektstruktur sieht jetzt so aus (überprüfen Sie es mit tree
/F):
218.
219.
220. [\MeinWorkspace\MvnSelenium]
221. |- [src]
222. |
'- [test]
223. |
'- [java]
224. |
'- [integrationtest]
225. |
|- SeleniumSimpleTest.java
226. |
227. '- pom.xml
228.
'- WebDriverWrapper.java
229.
230. Starten Sie den Integrationstest ohne Angabe eines Webbrowsers:
231.
cd \MeinWorkspace\MvnSelenium
mvn clean test
Sie erhalten einige Fehlermeldungen zur HTML-Seite und zuletzt die
Erfolgsmeldung:
-----------------------------------------------------------WebDriver:
HtmlUnitDriver
Test-URL:
http://de.wikipedia.org
Titel der Abfrage-Seite: Wikipedia – Die freie Enzyklopädie
Titel der Ergebnis-Seite: Selenium – Wikipedia
Hauptueberschrift:
Selenium
OK
------------------------------------------------------------
Als WebDriver wurde HtmlUnitDriver verwendet, der keine WebbrowserInstallation voraussetzt.
232.
233. Falls Sie Mozilla Firefox installiert haben, schließen Sie alle
Firefox-Fenster, und starten Sie den Integrationstest folgendermaßen:
234.
mvn clean test -D_BROWSERNAME=ff
Der Firefox-Webbrowser wird gestartet, Sie können die Seitenaufrufe
verfolgen, und es wird gemeldet,
dass der FirefoxDriver-WebDriver verwendet wurde:
...
WebDriver:
...
FirefoxDriver
235.
236. Falls Sie Windows 10 installiert haben, schließen Sie alle EdgeWebbrowser-Fenster, und starten Sie den Integrationstest
folgendermaßen:
237.
mvn clean test -D_BROWSERNAME=Edge
Der Edge-Webbrowser wird gestartet, Sie können die Seitenaufrufe
verfolgen, und es wird gemeldet,
dass der EdgeDriver-WebDriver verwendet wurde:
...
WebDriver:
...
EdgeDriver
Voraussetzung hierfür ist die einmalige Installation des MicrosoftWebDrivers
MicrosoftWebDriver.exe über die Installationsdatei
MicrosoftWebDriver.msi, welche Sie über
https://www.microsoft.com/en-us/download/details.aspx?id=48212
erhalten.
Automatisierter Integrationstest mit Fit
1.
2.
3.
4.
5.
6.
Ausführliche Infos zu Fit finden Sie in
java-fit.htm.
Wechseln Sie in Ihr Projekte-Verzeichnis und erzeugen Sie das neue
Projekt MvnFitTest:
7.
cd \MeinWorkspace
md MvnFitTest
cd MvnFitTest
md src\main\java\de\meinefirma\meinprojekt
md src\test\fit
md src\test\java\fixtures
tree /F
[\MeinWorkspace\MvnFitTest]
'- [src]
|- [main]
|
'- [java]
|
'- [de]
|
'- [meinefirma]
|
'- [meinprojekt]
'- [test]
|- [fit]
'- [java]
'- [fixtures]
8.
9.
Erzeugen Sie im MvnFitTest-Projektverzeichnis die
Projektkonfigurationsdatei: pom.xml
10.
11.
12. <project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
13.
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
14.
<modelVersion>4.0.0</modelVersion>
15.
<groupId>de.meinefirma.meinprojekt</groupId>
16.
<artifactId>MvnFitTest</artifactId>
17.
<version>1.0-SNAPSHOT</version>
18.
<packaging>jar</packaging>
19.
<name>MvnFitTest</name>
20.
<build>
21.
<plugins>
22.
<plugin>
23.
<groupId>org.codehaus.mojo</groupId>
24.
<artifactId>fit-maven-plugin</artifactId>
25.
<version>2.0-beta-3</version>
26.
<executions>
27.
<execution>
28.
<id>fixture</id>
29.
<phase>integration-test</phase>
30.
<configuration>
31.
<sourceDirectory>src/test/fit</sourceDirectory>
32.
<caseSensitive>true</caseSensitive>
33.
<sourceIncludes>**/*FitTest.html</sourceIncludes>
34.
<parseTags>
35.
<parseTag>table</parseTag>
36.
<parseTag>tr</parseTag>
37.
<parseTag>td</parseTag>
38.
</parseTags>
39.
<outputDirectory>target/fit</outputDirectory>
40.
<ignoreFailures>false</ignoreFailures>
41.
</configuration>
42.
<goals>
43.
<goal>run</goal>
44.
</goals>
45.
</execution>
46.
</executions>
47.
</plugin>
48.
</plugins>
49.
</build>
50.
<dependencies>
51.
<dependency>
52.
<groupId>com.c2.fit</groupId>
53.
<artifactId>fit</artifactId>
54.
<version>1.1</version>
55.
</dependency>
56.
</dependencies>
57. </project>
58.
59.
Erläuterungen zu den fit-maven-plugin-<configuration>-Optionen finden
Sie unter
http://mojo.codehaus.org/fit-maven-plugin/run-mojo.html.
Falls Sie die Fit-Tests nicht mit HTML-Seiten, sondern mit Wiki-Seiten
steuern wollen, finden Sie Konfigurationshinweise unter
http://mojo.codehaus.org/fit-maven-plugin/usage.html.
60.
61.
Erzeugen Sie im src\main\java\de\meinefirma\meinprojekt-Verzeichnis
die (schon bekannte) Bean: MeineBean.java
62.
63.
64. package de.meinefirma.meinprojekt;
65.
66. public class MeineBean
67. {
68.
public String berechne( String d1, String d2 )
69.
{
70.
try {
71.
return "" + (Double.parseDouble( d1 ) + Double.parseDouble(
d2 ));
72.
} catch( Exception ex ) {
73.
return "";
74.
}
75.
76. }
77.
78.
79.
}
Erzeugen Sie im src\test\java\fixtures-Verzeichnis die Fit-FixtureKlasse: MeinFixture.java
80.
81.
82. package fixtures;
83.
84. public class MeinFixture extends fit.ColumnFixture
85. {
86.
public String wert1;
87.
public String wert2;
88.
89.
public String berechne()
90.
{
91.
return (new de.meinefirma.meinprojekt.MeineBean()).berechne(
wert1, wert2 );
92.
}
93. }
94.
95.
96.
97.
Die Fit-Testbeschreibung erfolgt als HTML-Tabelle
(erste Zeile: Fixture-Angabe, zweite Zeile: Eingabeparameter und
erwartete zu berechnende Ausgabewerte, weitere Zeilen: Testfälle).
98.
Erzeugen Sie im src\test\fit-Verzeichnis die Fit-Testklasse:
MeinFitTest.html
<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso8859-1">
<title>Mein Fit-Test</title>
</head>
<body>
<h2>Mein Fit-Test</h2>
<table border="1" cellspacing="0" cellpadding="3">
<tr><td colspan="3"> fixtures.MeinFixture </td></tr>
<tr><td> wert1 </td><td> wert2 </td><td> berechne() </td></tr>
<tr><td>
0 </td><td>   </td><td>
  </td></tr>
<tr><td>
<tr><td>
<tr><td>
<tr><td>
</table>
1
+1
-42
1.234
</td><td>
</td><td>
</td><td>
</td><td>
2
-1
13
5.678
</td><td>
</td><td>
</td><td>
</td><td>
<br>
<table border="1" cellspacing="0" cellpadding="3">
<tr><td colspan="2">fit.Summary</td></tr>
</table>
</body>
</html>
Sehen Sie sich die Testfall-Beschreibungen an:
start src\test\fit\MeinFitTest.html
fixtures.MeinFixture
wert1 wert2 berechne()
0
1
2
3.0
+1
-1
0.0
-42
13
-29.0
1.234 5.678 6.912
99.
100.
101.
102.
103.
104.
105.
106.
107.
108.
109.
110.
111.
112.
Überprüfen Sie Ihre Projektstruktur mit tree /F:
[\MeinWorkspace\MvnFitTest]
|- [src]
|
|- [main]
|
|
'- [java]
|
|
'- [de]
|
|
'- [meinefirma]
|
|
'- [meinprojekt]
|
|
'- MeineBean.java
|
'- [test]
|
|- [fit]
3.0
0.0
-29.0
6.912
</td></tr>
</td></tr>
</td></tr>
</td></tr>
113.
114.
115.
116.
117.
118.
|
|
'- MeinFitTest.html
|
'- [java]
|
'- [fixtures]
|
'- MeinFixture.java
'- pom.xml
119.
120. Führen Sie den Akzeptanztest als Maven-Integrationstest aus:
121.
cd \MeinWorkspace\MvnFitTest
mvn clean verify
Sie erhalten:
[INFO] --- fit-maven-plugin:2.0-beta-3:run (fixture) @ MvnFitTest --[INFO] Running Fixture with input file src\test\fit\MeinFitTest.html
and output file target\fit\MeinFitTest.html
[INFO] ----------------------------------------------------------------------[INFO] BUILD SUCCESS
[INFO] -----------------------------------------------------------------------
122.
123. Sehen Sie sich das Ergebnis als HTML-Seite an (grün bedeutet "ok"):
124.
start target\fit\MeinFitTest.html
fixtures.MeinFixture
wert1 wert2 berechne()
0
null
1
2
3.0
+1
-1
0.0
-42
13
-29.0
1.234 5.678 6.912
fit.Summary
counts 4 right, 0 wrong, 0 ignored, 0 exceptions
125.
126. Ändern Sie in src\test\fit\MeinFitTest.html einen der Vorgabewerte in
der
berechne()-Spalte,
127.
führen Sie den Akzeptanztest erneut aus und sehen Sie sich die
Fehlermeldungen im Kommandozeilenfenster und in der Ergebnis-HTML-Seite
an,
128.
zum Beispiel so:
129.
130.
131. [INFO] [fit:run {execution: fixture}]
132. [INFO] Running Fixture with input file src\test\fit\MeinFitTest.html
and output file target\fit\MeinFitTest.html
133. [INFO] ----------------------------------------------------------------------134. [ERROR] BUILD ERROR
135. [INFO] ----------------------------------------------------------------------136. [INFO] Failed to execute FitRunner
137. Embedded error: Fixture failed with counts: 3 right, 1 wrong, 0
ignored, 0 exceptions
138.
fixtures.MeinFixture
wert1 wert2 berechne()
0
null
1
2
4.0 expected
3.0 actual
+1
-1
0.0
-42
13
-29.0
1.234 5.678 6.912
139.
fit.Summary
counts 3 right, 1 wrong, 0 ignored, 0 exceptions
140.
Automatisierter HTML-Akzeptanztest mit Jetty und Fit
1.
2.
Im letzten Beispiel hat das Fit-Fixture direkt die Berechnungs-Bean
angesprochen.
3.
Das folgende Beispiel setzt stattdessen auf einer höheren Ebene an
und ermittelt das Ergebnis über die HTML-Webseite.
4.
5. Voraussetzung für das folgende Beispiel ist die Durchführung der
beiden obigen Beispiele
6.
Automatisierter Integrationstest mit Jetty, HtmlUnit und HttpUnit
und
Automatisierter Integrationstest mit Fit.
7.
8.
Erzeugen Sie ein neues Projektverzeichnis MvnHtmlFitTest und kopieren
Sie dorthin Teile der beiden Projekte:
9.
cd \MeinWorkspace
md MvnHtmlFitTest
cd MvnHtmlFitTest
xcopy ..\MvnHtmlIntTest\*.* /S
xcopy ..\MvnFitTest\src\test\*.* src\test\ /S
tree /F
10.
11.
Ihre Projektstruktur sieht jetzt so aus:
12.
13.
14. [\MeinWorkspace\MvnHtmlFitTest]
15.
|- [src]
16.
|
|- [main]
17.
|
|
|- [java]
18.
|
|
|
'- [de]
19.
|
|
|
'- [meinefirma]
20.
|
|
|
'- [meinprojekt]
21.
|
|
|
'- MeineBean.java
22.
|
|
'- [webapp]
23.
|
|
|- [WEB-INF]
24.
|
|
|
'- web.xml
25.
|
|
|- BerechnungsFormular.jsp
26.
|
|
'- index.jsp
27.
|
'- [test]
28.
|
|- [fit]
29.
|
|
'- MeinFitTest.html
30.
|
'- [java]
31.
|
|- [de]
32.
|
|
'- [meinefirma]
33.
|
|
'- [meinprojekt]
34.
|
|
'- MeineBeanTest.java
35.
|
|- [fixtures]
36.
|
|
'- MeinFixture.java
37.
|
'- [integrationtest]
38.
|
|- HtmlUnitTest.java
39.
|
'- HttpUnitTest.java
40.
'- pom.xml
41.
42.
43.
44.
Fügen Sie aus der pom.xml des MvnFitTest-Projekts
sowohl den <plugin>... als auch den <dependency>...-Block ein in
die
45.
pom.xml des MvnHtmlFitTest-Projekts.
46.
47.
48.
Ändern Sie im src\test\java\fixtures-Verzeichnis die MeinFixture.java
zu:
49.
50.
51. package fixtures;
52.
53. public class MeinFixture extends fit.ColumnFixture
54. {
55.
public String wert1;
56.
public String wert2;
57.
58.
public String berechne() throws Exception
59.
{
60.
return (new integrationtest.HtmlUnitTest()).test( "8080", wert1,
wert2, null );
61.
}
62. }
63.
Statt HtmlUnitTest könnten Sie genauso gut auch HttpUnitTest nehmen.
Bitte beachten Sie, dass das Fit-Fixture jetzt nicht mehr direkt die
Berechnungs-Bean anspricht, sondern über die HTML-Webseite das Ergebnis
ermittelt.
64.
65.
66.
Führen Sie die Tests aus:
cd \MeinWorkspace\MvnHtmlFitTest
mvn clean verify
start target\fit\MeinFitTest.html
Sie erhalten die bereits oben gezeigten Erfolgsmeldungen.
Automatisierter Integrationstest mit Cargo für WebLogic,
JBoss, WildFly und Tomcat
Installation von Oracle WebLogic 12.2.1 (falls Sie WebLogic
verwenden wollen)
1.
2.
3.
4.
Installationsbeschreibungen finden Sie unter
Oracle WebLogic.
Installation von JBoss (falls Sie JBoss verwenden wollen)
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
Downloaden Sie den JBoss Application Server von
http://jboss.org/jbossas
(z.B. jboss-as-distribution-6.1.0.Final.zip).
Entzippen Sie das JBoss-Archive zum Beispiel nach
\Tools\JBoss.
Testen Sie die JBoss-Installation (passen Sie den Pfad zu Ihrer JBossInstallation an):
cd \Tools\JBoss\bin
run.bat (bzw. standalone.bat)
Warten Sie bis "... JBoss ... Started ..." erscheint.
start http://localhost:8080
Eine JBoss-Übersichtsseite erscheint.
Beenden Sie JBoss mit "Strg + C".
11.
12.
13.
14.
Weitere Installationshinweise (z.B. Datasource) finden Sie unter
jee-ejb2.htm#InstallationJBoss.
Installation von WildFly (falls Sie WildFly verwenden wollen)
1.
2.
3.
4.
5.
6.
7.
Downloaden Sie den WildFly Application Server von
http://www.wildfly.org
(z.B. wildfly-8.0.0.Beta1.zip).
Entzippen Sie das WildFly-Archive zum Beispiel nach
\Tools\WildFly.
8.
9.
Testen Sie die WildFly-Installation (passen Sie den Pfad zu Ihrer
WildFly-Installation an):
10.
cd \Tools\WildFly\bin
standalone.bat
Warten Sie bis "... WildFly ... Started ..." erscheint.
start http://localhost:8080
Eine WildFly-Welcome-Übersichtsseite erscheint.
Beenden Sie WildFly mit "Strg + C".
Installation von Tomcat (falls Sie Tomcat verwenden wollen)
1.
2.
3.
4.
Zum Tomcat finden Sie eine Installationsbeschreibung unter
jsp-install.htm#InstallationUnterWindows.
Automatisierter Integrationstest mit dem Cargo-Plugin sowie mit
HttpUnit und HtmlUnit
1.
2.
Voraussetzung für das folgende Beispiel ist die Installation von
WebLogic, JBoss, WildFly, Tomcat oder eines anderen Servers,
3.
den Cargo unterstützt.
4.
5.
6.
7.
8.
9.
10.
11.
Eine weitere Voraussetzung ist die Durchführung
(oder des Downloads) des obigen Beispiels
Automatisierter Integrationstest mit Jetty, HtmlUnit und HttpUnit.
Erzeugen Sie ein neues Projekt mit dem Projektverzeichnis
MvnAppSrvIntTest:
12.
cd \MeinWorkspace
md MvnAppSrvIntTest
cd MvnAppSrvIntTest
xcopy ..\MvnHtmlIntTest\*.* /S
13.
14.
15.
Ändern Sie Folgendes in der pom.xml:
Entfernen Sie den gesamten folgenden Jetty-<plugin>-Block (aber nicht
den maven-surefire-plugin-Block):
<plugin>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-maven-plugin</artifactId>
...
</plugin>
Fügen Sie stattdessen folgenden Cargo-<plugin>-Block ein:
<plugin>
<groupId>org.codehaus.cargo</groupId>
<artifactId>cargo-maven2-plugin</artifactId>
<version>1.6.3</version>
<configuration>
<container>
<containerId>${appsrv.containerId}</containerId>
<home>${appsrv.srvhome}</home>
</container>
<configuration>
<type>existing</type>
<home>${appsrv.dmnhome}</home>
<properties>
<!-- Falls WebLogic: Anpassen: -->
<cargo.weblogic.administrator.user>weblogic</cargo.weblogic.administrat
or.user>
<cargo.weblogic.administrator.password>weblogic0</cargo.weblogic.admini
strator.password>
<cargo.weblogic.server>AdminServer</cargo.weblogic.server>
<cargo.servlet.port>${port}</cargo.servlet.port>
</properties>
</configuration>
<wait>false</wait>
</configuration>
<executions>
<execution>
<id>start-container</id>
<phase>pre-integration-test</phase>
<goals>
<goal>start</goal>
</goals>
</execution>
<execution>
<id>stop-container</id>
<phase>post-integration-test</phase>
<goals>
<goal>stop</goal>
</goals>
</execution>
</executions>
</plugin>
Ersetzen Sie die Zeile
<port>8080</port>
durch:
<port>${port}</port>
Fügen Sie vor dem </project>-Ende-Tag ein (passen Sie die Pfade an
Ihre Application-Server-Installation an):
<profiles>
<profile>
<id>WebLogic</id>
<activation>
<property>
<name>env.APPSRV</name>
<value>WebLogic</value>
</property>
</activation>
<properties>
<!-- Falls WebLogic: Anpassen: -->
<appsrv.containerId>weblogic122x</appsrv.containerId>
<appsrv.srvhome>C:\WebLogic\wlserver</appsrv.srvhome>
<appsrv.dmnhome>C:\WebLogic\user_projects\domains\MeineDomain</appsrv.d
mnhome>
<port>7001</port>
</properties>
</profile>
<profile>
<id>JBoss</id>
<activation>
<property>
<name>env.APPSRV</name>
<value>JBoss</value>
</property>
</activation>
<properties>
<!-- Falls JBoss: Anpassen: -->
<appsrv.containerId>jboss61x</appsrv.containerId>
<appsrv.srvhome>D:\Tools\JBoss</appsrv.srvhome>
<appsrv.dmnhome>${appsrv.srvhome}/server/default</appsrv.dmnhome>
<port>8080</port>
</properties>
</profile>
<profile>
<id>WildFly</id>
<activation>
<property>
<name>env.APPSRV</name>
<value>WildFly</value>
</property>
</activation>
<properties>
<!-- Falls WildFly: Anpassen: -->
<appsrv.containerId>wildfly8x</appsrv.containerId>
<appsrv.srvhome>D:\Tools\WildFly</appsrv.srvhome>
<appsrv.dmnhome>${appsrv.srvhome}/standalone</appsrv.dmnhome>
<port>8080</port>
</properties>
</profile>
<profile>
<id>Tomcat</id>
<activation>
<property>
<name>env.APPSRV</name>
<value>Tomcat</value>
</property>
</activation>
<properties>
<!-- Falls Tomcat: Anpassen: -->
<appsrv.containerId>tomcat8x</appsrv.containerId>
<appsrv.srvhome>D:\Tools\Tomcat</appsrv.srvhome>
<appsrv.dmnhome>${appsrv.srvhome}</appsrv.dmnhome>
<port>8080</port>
</properties>
</profile>
</profiles>
Natürlich können Sie analog auch
andere Application Server einbinden.
16.
17.
18.
Lassen Sie sich anzeigen, welche Profile zur Verfügung stehen:
cd \MeinWorkspace\MvnAppSrvIntTest
mvn help:all-profiles
19.
20.
Führen Sie für Ihren App-Server (automatisiert) die Schritte des
Integrationstests aus (Vorbereitung, App-Server-Start, Deployment,
Integrationstest, App-Server-Stopp):
21.
mvn verify -P WebLogic
mvn verify -P JBoss
mvn verify -P WildFly
mvn verify -P Tomcat
Bitte beachten Sie, dass der App-Server automatisch hoch- und
heruntergefahren wird.
22.
23.
24.
25.
26.
Falls Sie eine Fehlermeldung erhalten ähnlich zu:
10.4.5 404 Not Found
The server has not found anything matching the Request-URI.
com.gargoylesoftware.htmlunit.FailingHttpStatusCodeException:
404 Not Found for http://localhost:7001/MvnHtmlIntTest/
Dann fügen Sie im fehlgeschlagenen Integrationstest (z.B.
HtmlUnitTest.java) vor der Zeile
"WebClient webClient = new WebClient();" die zusätzliche Zeile
"Thread.sleep( 3000 );" ein, damit der Webserver das Deployment
vollständig fertigstellen kann.
27.
28.
Im Falle des JBoss Application Servers erhalten Sie:
29.
30.
31. ...
32. [INFO] --- maven-surefire-plugin:2.20:test (default-test) @
MvnHtmlIntTest --33. ------------------------------------------------------34.
T E S T S
35. ------------------------------------------------------36. Running de.meinefirma.meinprojekt.MeineBeanTest
37. Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
38. ...
39. [INFO] [stalledLocalDeployer] Deploying
[\MeinWorkspace\MvnAppSrvIntTest\target\MvnHtmlIntTest.war] to
[\Tools\JBoss/server/default/deploy]...
40. ...
41. [INFO] [talledLocalContainer] JBoss 6.1.0 started on port [8080]
42. ...
43. [INFO] --- maven-surefire-plugin:2.20:test (integration-tests) @
MvnHtmlIntTest --44. [INFO] Surefire report directory:
\MeinWorkspace\MvnAppSrvIntTest\target\surefire-reports
45. ------------------------------------------------------46.
T E S T S
47. ------------------------------------------------------48. Running integrationtest.HtmlUnitTest
49. Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
50. Running integrationtest.HttpUnitTest
51. Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
52. Results :
53. Tests run: 2, Failures: 0, Errors: 0, Skipped: 0
54. ...
55. [INFO] [talledLocalContainer] JBoss 6.1.0 is stopped
56. [INFO] ----------------------------------------------------------------------57. [INFO] BUILD SUCCESS
58. [INFO] ----------------------------------------------------------------------59.
60.
61.
Das .war-Archiv wurde in Autodeploy-Verzeichnisse unterhalb von
${appsrv.dmnhome} kopiert:
62.
Im Falle von WebLogic nach: ${appsrv.dmnhome}\autodeploy,
63.
im Falle von JBoss nach: ${appsrv.dmnhome}\deploy,
64.
im Falle von WildFly nach: ${appsrv.dmnhome}\deployments und
65.
66.
im Falle von Tomcat nach: ${appsrv.dmnhome}\webapps.
67.
68.
69.
Unter selenium.htm finden Sie weitere Cargo-Beispiele,
zum Beispiel mit dem zipUrlInstaller, mit dem Sie die Installation
von Servern automatisieren können.
70.
71.
72.
73.
74.
75.
76.
77.
78.
Cargo bietet einige weitere interessante Optionen, beispielsweise das
Mergen von WAR-Dateien und die Möglichkeit,
Tomcat im Security Mode zu starten.
Weiters zum Cargo-Maven-Plugin finden Sie unter
http://cargo.codehaus.org/Maven2+plugin und
http://cargo.codehaus.org/Maven2+Plugin+Reference+Guide.
79.
80.
81.
82.
83.
Sehen Sie sich auch die Beispiele zu Tests mit
OpenEJB und
Arquillian an.
Integrationstests mit dem Maven Failsafe Plugin
Anforderungen an Integrationstests
Häufige Anforderungen an Integrationstests im Maven-Umfeld sind:
1.
2.
3.
Trennung der verschiedenen Testebenen:




"Unittests" (= "Modultests", "Komponententests"), die ohne
weitere Systeme funktionsfähig sind und so schnell sind,
dass sie "häufig" aufgerufen werden, und
"Integrationstests" (eventuell auch "Systemtests"), die nur
zusammen mit weiteren Systemen (z.B. DB, App-Server etc.)
ausgeführt werden können und seltener aufgerufen werden
(z.B. in Continuous-Integration-Systemen).
4. "mvn test" soll nur die "schnellen Unittests" ausführen.
5. "mvn verify" soll die "schnellen Unittests" und die "Integrationstests"
ausführen
6.
(normalerweise sollte nicht "mvn integration-test", sondern
stattdessen "mvn verify" verwendet werden,
7.
damit auch die "post-integration-test"-Phase ausgeführt wird).
8. Wünschenswert wäre, wenn bei "mvn install" die Wahl besteht,
9.
ob hierbei nur die "schnellen Unittests" oder auch die
"Integrationstests" ausgeführt werden.
10. Wünschenswert wäre, wenn bei "mvn verify" im Falle fehlschlagender
Integrationstests der Testlauf nicht einfach abgebrochen wird,
11.
sondern trotzdem die der integration-test-Phase nachgeschaltete
post-integration-test-Phase ausgeführt wird,
12.
damit Ressourcen korrekt geschlossen und beendet werden, und erst
danach in der verify-Phase abbgebrochen wird.
Integrationstests mit dem Surefire-Plugin
Mit dem Maven Surefire Plugin
lassen sich die ersten drei Anforderungen nur umständlich und die letzten
beiden Anforderungen so gut wie gar nicht umsetzen.
Siehe hierzu das obige Beispiel eines
Automatisierten Integrationstests.
Der wichtigste Abschnitt in der pom.xml lautet dort (gekürzt):
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.20</version>
<configuration>
<excludes>
<exclude>**/integrationtest/*Test.java</exclude>
</excludes>
</configuration>
<executions>
<execution>
<id>integration-tests</id>
<phase>integration-test</phase>
<goals>
<goal>test</goal>
</goals>
<configuration>
<skip>false</skip>
<excludes>
<exclude>none</exclude>
</excludes>
<includes>
<include>**/integrationtest/*Test.java</include>
</includes>
</configuration>
</execution>
</executions>
</plugin>
Integrationstests mit dem Failsafe-Plugin
Mit dem Maven Failsafe Plugin
lassen sich alle fünf Anforderungen umsetzen.
Dazu muss der maven-surefire-plugin-Block entfernt werden und
durch folgenden maven-failsafe-plugin-Block ersetzt werden:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<version>2.20</version>
<executions>
<execution>
<goals>
<goal>integration-test</goal>
<goal>verify</goal>
</goals>
</execution>
</executions>
</plugin>
Außerdem müssen für die Dateinamen folgende Konventionen beachtet werden
(die auch anders konfiguriert werden können):


"**/Test*.java", "**/*Test.java", "**/*TestCase.java": für "Unittests",
siehe Surefire Plugin: Inclusions of Tests.

"**/IT*.java", "**/*IT.java", "**/*ITCase.java": für
"Integrationstests",
siehe Failsafe Plugin: Inclusions of Tests.

Defaultmäßig werden beim "mvn install"-Kommando die Integrationstests immer
vorher ausgeführt (integration-test- und verify-Phasen).
Bei Verwendung des Failsafe-Plugins können die Integrationstests
übergangen werden mit: "mvn install -DskipITs".
Anwendung auf den obigen Integrationstest mit Cargo
Folgendermaßen können Sie obigen Automatisierten Integrationstest mit Cargo
auf das Failsafe-Plugin umstellen:
1.
2.
Entfernen Sie aus der pom.xml den kompletten maven-surefire-pluginBlock und
3.
fügen Sie stattdessen einen maven-failsafe-plugin-Block ein.
4.
Allerdings müssen Sie dabei für dieses spezielle Beispiel die
Konfiguration der Portnummer über die System-Environmentvariable
übernehmen:
5.
6.
7.
<plugin>
8.
<groupId>org.apache.maven.plugins</groupId>
9.
<artifactId>maven-failsafe-plugin</artifactId>
10.
<version>2.20</version>
11.
<configuration>
12.
<systemPropertyVariables>
13.
<port>${port}</port>
14.
</systemPropertyVariables>
15.
</configuration>
16.
<executions>
17.
<execution>
18.
<goals>
19.
<goal>integration-test</goal>
20.
<goal>verify</goal>
21.
22.
23.
24.
25.
</goals>
</execution>
</executions>
</plugin>
26.
27.
Im src\test\java\integrationtest-Verzeichnis müssen zu
HtmlUnitTest.java und HttpUnitTest.java
28.
sowohl die beiden Dateinamen als auch in beiden Dateien die
Klassennamen umbenannt werden in HtmlUnitIT bzw. HttpUnitIT.
29.
30.
31.
Die Projektstruktur sieht jetzt so aus (überprüfen Sie es mit tree
/F):
32.
33.
34. [\MeinWorkspace\MvnAppSrvFailsaveIntTest]
35.
|- [src]
36.
|
|- [main]
37.
|
|
|- [java]
38.
|
|
|
'- [de]
39.
|
|
|
'- [meinefirma]
40.
|
|
|
'- [meinprojekt]
41.
|
|
|
'- MeineBean.java
42.
|
|
'- [webapp]
43.
|
|
|- [WEB-INF]
44.
|
|
|
'- web.xml
45.
|
|
|- BerechnungsFormular.jsp
46.
|
|
'- index.jsp
47.
|
'- [test]
48.
|
'- [java]
49.
|
|- [de]
50.
|
|
'- [meinefirma]
51.
|
|
'- [meinprojekt]
52.
|
|
'- MeineBeanTest.java
53.
|
'- [integrationtest]
54.
|
|- HtmlUnitIT.java
55.
|
'- HttpUnitIT.java
56.
'- pom.xml
57.
58.
59. Führen Sie folgende Kommandos aus (bitte Profilnamen entsprechend
Ihres App-Servers wählen):
60.
Es werden nur die auf "Test" endenden "schnellen
Unittests" ausgeführt.
Es werden die Unittests und die beiden auf "IT"
mvn verify -P WebLogic
endenden Integrationstests ausgeführt.
Vor der Installation des Ergebnisartefakts im lokalen
mvn install -P WebLogic
Repository werden die Unittests und die
Integrationstests ausgeführt.
Vor der Installation des Ergebnisartefakts im lokalen
Repository werden nur die Unittests ausgeführt, die
Integrationstests werden ausgelassen. Es erscheint die
mvn install -P WebLogic -DskipITs
Meldung:
maven-failsafe-plugin: Tests are
skipped.
mvn test
61.
62.
63.
Ändern Sie src\test\java\integrationtest\HttpUnitIT.java so ab, dass
der Test fehlschlägt.
64.
Führen Sie wieder "mvn verify -P WebLogic" aus:
65.
Anders als mit dem Surefire-Plugin wird trotz des fehlschlagenden
Tests zuerst der WebLogic ordentlich heruntergefahren,
66.
bevor das Maven-Kommando in der verify-Phase den Fehler meldet und
abbricht.
67.
68.
69.
Grundsätzlich sollten Sie immer das gewählte Character-Encoding in
der pom.xml definieren
70.
(so vermeiden Sie auch die Warnungen des Failsafe-Plugins wegen
fehlender Angaben zum Encoding),
71.
zum Beispiel so (passen Sie das Encoding an):
72.
73.
74.
<properties>
75.
<project.build.sourceEncoding>ISO-88591</project.build.sourceEncoding>
76.
<project.reporting.outputEncoding>UTF8</project.reporting.outputEncoding>
77.
</properties>
78.
Webapp mit Wicket
Mit Apache Wicket
können komponentenbasierte Webanwendungen erstellt werden, inklusive
AJAX-Unterstützung.
Der Entwickler hat dabei nur mit Java und HTML zu tun und benötigt kein
JSP, JSF und JavaScript.
Weiteres zu Wicket finden Sie unter
http://wicket.apache.org und
http://wicket.sourceforge.net/apidocs.
Das Maven-Archetype-Plugin bietet Unterstützung für
Apache Wicket.
Das folgende Beispiel implementiert wieder das bereits bekannte Webformular,
welches wieder über eine MeineBean.berechne()-Methode zwei Zahlen addiert
(stellvertretend für Businesslogik).
1.
2.
3.
Generieren Sie ein Wicket-Projekt:
cd \MeinWorkspace
mvn archetype:generate -DinteractiveMode=false DarchetypeGroupId=org.apache.wicket -DarchetypeArtifactId=wicketarchetype-quickstart -DgroupId=de.meinefirma.meinprojekt DartifactId=MvnWicket
cd MvnWicket
tree /F
[\MeinWorkspace\MvnWicket]
|- [src]
|
|- [main]
|
|
|- [java]
|
|
|
'- [de]
|
|
|
'- [meinefirma]
|
|
|
'- [meinprojekt]
|
|
|
|- HomePage.html
|
|
|
|- HomePage.java
|
|
|
'- WicketApplication.java
|
|
|- [resources]
|
|
|
'- log4j2.xml
|
|
'- [webapp]
|
|
|- [WEB-INF]
|
|
|
'- web.xml
|
|
'- logo.png
|
|
'- style.css
|
'- [test]
|
|- [java]
|
|
'- [de]
|
|
'- [meinefirma]
|
|
'- [meinprojekt]
|
|
|- Start.java
|
|
'- TestHomePage.java
|
|- [jetty]
|
|
|- jetty-http.xml
|
|
|- jetty-https.xml
|
|
|- jetty-ssl.xml
|
|
'- jetty.xml
|
'- [resources]
|
'- keystore
'- pom.xml
4.
5.
6.
Testen Sie die vorläufige Quickstart-Webanwendung:
mvn jetty:run
start http://localhost:8080
-->
...
Congratulations!
Your quickstart works!
...
Beenden Sie Jetty mit "Strg + C".
7.
8.
Fügen Sie im src\main\java\de\meinefirma\meinprojekt-Verzeichnis in
der HomePage.html
9.
an geeigneter Stelle (z.B. hinter der "<h2>Congratulations!</h2>"Zeile) folgende Zeile hinzu:
10.
11.
12. <p><wicket:link><a href="BerechnungsFormular.html">Mein
Berechnungsformular</a></wicket:link></p>
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
Erzeugen Sie im src\main\java\de\meinefirma\meinprojekt-Verzeichnis
folgende BerechnungsFormular.html:
<html>
<head><title>Webseite mit Berechnungsformular</title></head>
<body>
<h2>Webseite mit Berechnungsformular</h2>
<form wicket:id="meinFormular" method="post"><pre>
Wert1 <input wicket:id="wert1"
type="text"
size=10
maxlength=10><br>
24. Wert2 <input wicket:id="wert2"
type="text"
size=10
maxlength=10><br>
25.
<input wicket:id="berechne" type="submit" value="berechne">
<input wicket:id="ergebnis" type="text" size=10 readonly><br>
26. </pre></form>
27. <span wicket:id="FehlerFeedback"/>
28. </body>
29. </html>
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
Erzeugen Sie im src\main\java\de\meinefirma\meinprojekt-Verzeichnis
folgende BerechnungsFormular.java:
package de.meinefirma.meinprojekt;
import
import
import
import
import
java.io.Serializable;
org.apache.wicket.markup.html.WebPage;
org.apache.wicket.markup.html.form.*;
org.apache.wicket.markup.html.panel.FeedbackPanel;
org.apache.wicket.model.CompoundPropertyModel;
public class BerechnungsFormular extends WebPage
{
public BerechnungsFormular()
{
Form<MeinDatenModel> form = new Form<MeinDatenModel>(
"meinFormular" ) {
48.
private static final long serialVersionUID = 1L;
49.
@Override protected void onSubmit() {
50.
MeinDatenModel datenModel = getModelObject();
51.
datenModel.ergebnis = (new MeineBean()).berechne(
datenModel.wert1, datenModel.wert2 );
52.
}
53.
};
54.
form.setModel( new CompoundPropertyModel<MeinDatenModel>( new
MeinDatenModel() ) );
55.
form.add( new RequiredTextField<Double>( "wert1" ) );
56.
form.add( new RequiredTextField<Double>( "wert2" ) );
57.
form.add( new Button( "berechne" ) );
58.
form.add( new TextField<Double>( "ergebnis" ) );
59.
add( form );
60.
add( new FeedbackPanel( "FehlerFeedback" ) );
61.
}
62.
63.
class MeinDatenModel implements Serializable
64.
{
65.
private static final long serialVersionUID = 1L;
66.
public Double wert1, wert2, ergebnis;
67.
}
68. }
69.
70.
71.
72.
73.
Erzeugen Sie im src\main\java\de\meinefirma\meinprojekt-Verzeichnis
MeineBean.java:
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
package de.meinefirma.meinprojekt;
public class MeineBean
{
public Double berechne( Number d1, Number d2 )
{
return ( d1 == null || d2 == null ) ? null
: new Double( d1.doubleValue() + d2.doubleValue() );
}
}
Testen Sie die Wicket-Webanwendung:
mvn clean jetty:run
start http://localhost:8080
Klicken Sie auf Mein Berechnungsformular, tragen Sie zwei Zahlen ein
und lassen Sie die Summe berechnen.
Sie können auch Dezimalzahlen eingeben: Dabei müssen Sie als
Dezimalpunkt bei deutscher Webbrowser-Einstellung ein Komma (und keinen
Punkt) verwenden.
Lassen Sie ein Feld leer, tragen Sie in das andere Feld Buchstaben ein
und sehen Sie sich die Fehlermeldungen an.
Beenden Sie Jetty mit "Strg + C".
Vert.x-HelloWorld
Vert.x ist ein reaktives, modulares, nachrichtenorientiertes, polyglottes
Framework,
mit dem sich klassische Anwendungen, Webanwendungen und Microservices
realisieren lassen.
Infos zu Vert.x finden Sie in der
Vert.x Documentation und im
Vert.x Tutorial von Jakob Jenkov.
Das folgende Beispiel zeigt:



eine minimale
Vert.x-Hello-World-Webanwendung
inklusive Webserver,



und wie mit dem
maven-shade-plugin
eine direkt ausführbare "Fat-Jar" inklusive aller Abhängigkeiten
erstellt werden kann.
Führen Sie folgende Schritte aus:
1.
2.
3.
Legen Sie ein Projektverzeichnis MvnVertxHelloWorld an und erzeugen
Sie darin folgende Verzeichnisstruktur:
cd \MeinWorkspace
md MvnVertxHelloWorld
cd MvnVertxHelloWorld
md src\main\java\de\meinefirma\meinprojekt
4.
5.
Erzeugen Sie im MvnVertxHelloWorld-Projektverzeichnis folgende
Projektkonfigurationsdatei: pom.xml
6.
7.
8. <?xml version="1.0" encoding="UTF-8"?>
9. <project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
10.
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
11.
<modelVersion>4.0.0</modelVersion>
12.
<groupId>de.meinefirma.meinprojekt</groupId>
13.
<artifactId>MvnVertxHelloWorld</artifactId>
14.
<version>1.0-SNAPSHOT</version>
15.
<properties>
16.
<exec.mainClass>de.meinefirma.meinprojekt.HelloWorldVertx</exec.mainCla
ss>
17.
</properties>
18.
<dependencies>
19.
<dependency>
20.
<groupId>io.vertx</groupId>
21.
<artifactId>vertx-core</artifactId>
22.
<version>3.4.1</version>
23.
</dependency>
24.
</dependencies>
25.
<build>
26.
<pluginManagement>
27.
<plugins>
28.
<plugin>
29.
<artifactId>maven-compiler-plugin</artifactId>
30.
<version>3.6.1</version>
31.
<configuration>
32.
<source>1.8</source>
33.
<target>1.8</target>
34.
</configuration>
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
</plugin>
</plugins>
</pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.0.0</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer
implementation="org.apache.maven.plugins.shade.resource.ManifestResourc
eTransformer">
52.
<manifestEntries>
53.
<Main-Class>${exec.mainClass}</Main-Class>
54.
</manifestEntries>
55.
</transformer>
56.
<transformer
implementation="org.apache.maven.plugins.shade.resource.AppendingTransf
ormer">
57.
<resource>METAINF/services/io.vertx.core.spi.VerticleFactory</resource>
58.
</transformer>
59.
</transformers>
60.
<artifactSet>
61.
</artifactSet>
62.
<outputFile>${project.build.directory}/${project.artifactId}${project.version}-fat.jar</outputFile>
63.
</configuration>
64.
</execution>
65.
</executions>
66.
</plugin>
67.
</plugins>
68.
</build>
69. </project>
70.
71.
72.
Erzeugen Sie im src\main\java\de\meinefirma\meinprojekt-Verzeichnis
folgende Vert.x-Klasse: HelloWorldVertx.java
73.
74.
75. package de.meinefirma.meinprojekt;
76.
77. import io.vertx.core.Vertx;
78.
79. public class HelloWorldVertx
80. {
81.
public static void main( String[] args )
82.
{
83.
Vertx.vertx().createHttpServer().requestHandler( req ->
req.response().end( "Hello World." ) ).listen( 8080 );
84.
}
85. }
86.
87.
88.
89.
Ihre Projektverzeichnisstruktur sieht jetzt so aus:
tree /F
[\MeinWorkspace\MvnVertxHelloWorld]
|- [src]
|
'- [main]
|
'- [java]
|
'- [de]
|
'- [meinefirma]
|
'- [meinprojekt]
|
'- HelloWorldVertx.java
'- pom.xml
90.
91.
92.
Rufen Sie im Kommandozeilenfenster auf::
cd \MeinWorkspace\MvnVertxHelloWorld
mvn clean package
java -jar target/MvnVertxHelloWorld-1.0-SNAPSHOT-fat.jar
start http://localhost:8080
Es wird ein Webserver mit der Vert.x-Webanwendung gestartet und Sie
erhalten im Webbrowser:
Hello World.
93.
94.
95.
96.
In realen
Vert.x-Anwendungen
werden statt der hier gezeigten einfachen Variante die
Funktionalitäten normalerweise in
97.
Verticles
98.
aufgeteilt.
99.
OSGi-Bundle mit dem Maven-Bundle-Plugin
OSGi ist ein leistungsfähiges dynamisches Komponentensystem für Java.
Im Folgenden wird anhand eines kurzen Beispiels gezeigt, wie mit dem
Maven-Bundle-Plugin des Apache-Felix-Projekts
ein OSGi-Bundle erstellt werden kann.
1.
2.
Falls Sie noch kein OSGi installiert haben:
3.
Sie können wahlweise eine komplette OSGi-Umgebung installieren, zum
Beispiel wie für
4.
Eclipse Equinox
5.
beschrieben.
6.
Für dieses einfache Beispiel genügt es, wenn Sie alternativ
lediglich eine einzelne OSGi-jar-Bibliothek downloaden
7.
und in Ihr Projektverzeichnis kopieren, nämlich die
8.
org.eclipse.osgi_3.9.1.v20140110-1610.jar von
9.
http://archive.eclipse.org/equinox/drops/R-KeplerSR2-201402211700.
10.
Achtung: Bei neueren OSGi-Framework-Versionen lässt sich leider
nicht mehr ganz so einfach wie im folgenden Beispiel
11.
die Konsole starten, siehe hierzu:
12.
"Incompatibilities" / "5. Built-in Equinox OSGi console removed".
13.
14.
15.
Legen Sie ein Projektverzeichnis MvnOsgi an und erzeugen Sie darin
folgende Verzeichnisstruktur:
16.
cd \MeinWorkspace
md MvnOsgi\configuration
md MvnOsgi\src\main\java\de\meinefirma\meinprojekt
cd MvnOsgi
tree /F
[\MeinWorkspace\MvnOsgi]
|- [configuration]
|- [src]
|
'- [main]
|
'- [java]
|
'- [de]
|
'- [meinefirma]
|
'- [meinprojekt]
'- org.eclipse.osgi_3.9.1.v20140110-1610.jar
OSGi-Installation]
17.
18.
[falls keine andere
Erzeugen Sie im MvnOsgi-Projektverzeichnis folgende
Projektkonfigurationsdatei: pom.xml
19.
20.
21. <project>
22.
<modelVersion>4.0.0</modelVersion>
23.
<groupId>de.meinefirma.meinprojekt</groupId>
24.
<artifactId>MvnOsgi</artifactId>
25.
<version>1.0-SNAPSHOT</version>
26.
<packaging>bundle</packaging>
27.
<name>MvnOsgi</name>
28.
<build>
29.
<plugins>
30.
<plugin>
31.
<groupId>org.apache.felix</groupId>
32.
<artifactId>maven-bundle-plugin</artifactId>
33.
<version>3.0.1</version>
34.
<extensions>true</extensions>
35.
<configuration>
36.
<instructions>
37.
<!-- Falls Sie etwas zu exportieren haben:
38.
<Export-Package>de.meinefirma.meinprojekt.intf...</ExportPackage> -->
39.
<Private-Package>de.meinefirma.meinprojekt.*</PrivatePackage>
40.
<BundleActivator>de.meinefirma.meinprojekt.Activator</Bundle-Activator>
41.
</instructions>
42.
</configuration>
43.
</plugin>
44.
</plugins>
45.
</build>
46.
<dependencies>
47.
<dependency>
48.
<groupId>org.apache.felix</groupId>
49.
<artifactId>org.osgi.core</artifactId>
50.
<version>1.4.0</version>
51.
</dependency>
52.
</dependencies>
53. </project>
54.
55.
56.
Erzeugen Sie im configuration-Verzeichnis folgende OSGiKonfigurationsdatei: config.ini
57.
58.
59. osgi.console.enable.builtin=true
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
Erzeugen Sie im src\main\java\de\meinefirma\meinprojekt-Verzeichnis
folgende Bundle-Java-Klasse: Activator.java
package de.meinefirma.meinprojekt;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
public class Activator implements BundleActivator
{
@Override
public void start( BundleContext context ) throws Exception
{
System.out.println( context.getBundle().getSymbolicName() +
Hallo OSGi-Welt." );
76.
}
77.
78.
@Override
79.
public void stop( BundleContext context ) throws Exception
80.
{
81.
System.out.println( context.getBundle().getSymbolicName() +
Tschau OSGi-Welt." );
82.
}
83. }
84.
85.
86.
87.
Ihre Projektverzeichnisstruktur sieht jetzt so aus:
tree /F
[\MeinWorkspace\MvnOsgi]
|- [configuration]
|
'- config.ini
|- [src]
|
'- [main]
|
'- [java]
":
":
|
'- [de]
|
'- [meinefirma]
|
'- [meinprojekt]
|
'- Activator.java
|- org.eclipse.osgi_3.9.1.v20140110-1610.jar
OSGi-Installation]
'- pom.xml
88.
89.
[falls keine andere
Geben Sie im Kommandozeilenfenster (und in der OSGi-Konsole)
Folgendes ein (passen Sie den OSGi-Dateinamen an):
90.
cd \MeinWorkspace\MvnOsgi
mvn package
java -jar org.eclipse.osgi_3.9.1.v20140110-1610.jar -console -clean
install file:target\MvnOsgi-1.0-SNAPSHOT.jar start
ss
uninstall 1
close
Sie erhalten:
osgi> install file:target\MvnOsgi-1.0-SNAPSHOT.jar start
Bundle id is 1
de.meinefirma.meinprojekt.MvnOsgi: Hallo OSGi-Welt.
osgi> ss
id
State
Bundle
0
ACTIVE
org.eclipse.osgi_3.9.1.v20140110-1610
1
ACTIVE
de.meinefirma.meinprojekt.MvnOsgi_1.0.0.SNAPSHOT
osgi> uninstall 1
de.meinefirma.meinprojekt.MvnOsgi: Tschau OSGi-Welt.
osgi> close
91.
92.
Falls Sie nicht die org.eclipse.osgi_3.9.1.v20140110-1610.jar
downgeloaded haben, sondern eine andere OSGi-Installation verwenden
wollen,
93.
müssen Sie das java...-Kommando entsprechend ändern,
94.
zum Beispiel für eine Installation entsprechend der
95.
Beschreibung zu Eclipse Equinox folgendermaßen
96.
(passen Sie den Pfad und den OSGi-Dateinamen an):
97.
java -jar C:\Tools\equinoxSDK\plugins\org.eclipse.osgi_3.9.1.v20140110-1610.jar -console -clean
98.
99.
Bei Problemen sehen Sie sich die .log-Datei im configurationUnterverzeichnis an.
100.
101.
102. Erläuterungen finden Sie beim nahezu identischen
103.
OSGi-HelloWorld-Programmierbeispiel.
104.
Dort sind auch weitere ausführbare Kommandos und weitere OSGiProgrammierbeispiele aufgeführt.
105.
Suche mit Lucene
Das Java-basierende Lucene
zählt zu den bekanntesten Text-Suchmaschinen.
Lucene ist Open Source, sehr leistungsfähig und enthält viele Features.
Das folgende "Lucene-HelloWorld-Programmierbeispiel" zeigt eine einfache
Textsuche über Dateien in einem Verzeichnis inklusive der Unterverzeichnisse.
1.
2.
Legen Sie ein Projektverzeichnis MvnLucene an und erzeugen Sie darin
folgende Verzeichnisstruktur:
3.
cd \MeinWorkspace
md MvnLucene\src\main\java\de\meinefirma\meinprojekt
md MvnLucene\src\test\java\de\meinefirma\meinprojekt
cd MvnLucene
tree /F
[\MeinWorkspace\MvnLucene]
'- [src]
|- [main]
|
'- [java]
|
'- [de]
|
'- [meinefirma]
|
'- [meinprojekt]
'- [test]
'- [java]
'- [de]
'- [meinefirma]
'- [meinprojekt]
4.
5.
Erzeugen Sie im MvnLucene-Projektverzeichnis folgende
Projektkonfigurationsdatei: pom.xml
6.
7.
8. <project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
9.
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
10.
<modelVersion>4.0.0</modelVersion>
11.
<groupId>de.meinefirma.meinprojekt</groupId>
12.
<artifactId>MvnLucene</artifactId>
13.
<version>1.0-SNAPSHOT</version>
14.
<packaging>jar</packaging>
15.
<name>MvnLucene</name>
16.
<build>
17.
<plugins>
18.
<plugin>
19.
<artifactId>maven-assembly-plugin</artifactId>
20.
<version>3.0.0</version>
21.
<configuration>
22.
<descriptorRefs>
23.
<descriptorRef>jar-with-dependencies</descriptorRef>
24.
</descriptorRefs>
25.
<archive>
26.
<manifest>
27.
<mainClass>de.meinefirma.meinprojekt.LuceneSimpleUtil</mainClass>
28.
</manifest>
29.
</archive>
30.
</configuration>
31.
<executions>
32.
<execution>
33.
<id>make-assembly</id>
34.
<phase>package</phase>
35.
<goals>
36.
<goal>single</goal>
37.
</goals>
38.
</execution>
39.
</executions>
40.
</plugin>
41.
</plugins>
42.
</build>
43.
<dependencies>
44.
<dependency>
45.
<groupId>org.apache.lucene</groupId>
46.
<artifactId>lucene-core</artifactId>
47.
<version>4.10.4</version>
48.
</dependency>
49.
<dependency>
50.
<groupId>org.apache.lucene</groupId>
51.
<artifactId>lucene-queryparser</artifactId>
52.
<version>4.10.4</version>
53.
</dependency>
54.
<dependency>
55.
<groupId>org.apache.lucene</groupId>
56.
<artifactId>lucene-analyzers-common</artifactId>
57.
<version>4.10.4</version>
58.
</dependency>
59.
<dependency>
60.
<groupId>junit</groupId>
61.
<artifactId>junit</artifactId>
62.
<version>4.12</version>
63.
<scope>test</scope>
64.
</dependency>
65.
</dependencies>
66. </project>
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
Erzeugen Sie im src\main\java\de\meinefirma\meinprojekt-Verzeichnis
folgende Java-Klasse: LuceneSimpleUtil.java
package de.meinefirma.meinprojekt;
import
import
import
import
import
import
import
import
import
import
import
import
java.io.*;
java.nio.charset.Charset;
java.util.*;
org.apache.lucene.analysis.Analyzer;
org.apache.lucene.analysis.standard.StandardAnalyzer;
org.apache.lucene.document.*;
org.apache.lucene.index.*;
org.apache.lucene.index.IndexWriterConfig.OpenMode;
org.apache.lucene.queryparser.classic.QueryParser;
org.apache.lucene.search.*;
org.apache.lucene.store.*;
org.apache.lucene.util.Version;
public class LuceneSimpleUtil
{
public static void main( String[] args ) throws Exception
{
if( args.length != 2 && args.length != 3 ) {
System.out.println(
"\nIndizieren mit 3 Parametern:\n" +
" PfadZuDokumenten CharacterEncoding PfadZumIndex\n" +
"Suchen mit 2 Parametern:\n" +
" PfadZumIndex SuchWort\n" );
}
98.
99.
if( args.length == 3 ) {
System.out.println( "\nIndexerstellung im Verzeichnis:
" + args[2] );
100.
System.out.println(
"Zu indizierende Dateien im
Verzeichnis: " + args[0] );
101.
System.out.println(
"Character-Encoding:
" + args[1] );
102.
System.out.println(
"Indizierte Dateien:" );
103.
System.out.println( LuceneSimpleUtil.indexFiles( args[0],
args[1], args[2], true ) );
104.
}
105.
if( args.length == 2 ) {
106.
System.out.println( "\nIndex im Verzeichnis: " + args[0] );
107.
System.out.println( "Suchwort '" + args[1] + "' gefunden in:"
);
108.
List<Document> docs = LuceneSimpleUtil.searchFiles( args[0],
args[1], 100 );
109.
for( int i = 0; i < docs.size(); i++ ) {
110.
System.out.println( docs.get( i ).get( "path" ) );
111.
}
112.
}
113.
}
114.
115.
public static List<Document> searchFiles( String indexPath, String
queryString, int maxResults ) throws Exception
116.
{
117.
if( queryString == null || queryString.trim().length() == 0 ) {
return null; }
118.
List<Document> docs
= new ArrayList<Document>();
119.
IndexReader
reader
= DirectoryReader.open(
FSDirectory.open( new File( indexPath ) ) );
120.
IndexSearcher searcher = new IndexSearcher( reader );
121.
Analyzer
analyzer = new StandardAnalyzer(
Version.LUCENE_4_9 );
122.
QueryParser
parser
= new QueryParser( Version.LUCENE_4_9,
"contents", analyzer );
123.
Query
query
= parser.parse( queryString.trim() );
124.
TopDocs
results = searcher.search( query, maxResults );
125.
ScoreDoc[]
hits
= results.scoreDocs;
126.
for( ScoreDoc scoreDoc : hits ) {
127.
docs.add( searcher.doc( scoreDoc.doc ) );
128.
}
129.
reader.close();
130.
analyzer.close();
131.
return docs;
132.
}
133.
134.
public static List<File> indexFiles( String docsPath, String
charEncoding, String indexPath, boolean createNew ) throws IOException
135.
{
136.
if( docsPath == null ) { return null; }
137.
final File docDir = new File( docsPath );
138.
if( !docDir.exists() || !docDir.canRead() ) { return null; }
139.
Directory dir = FSDirectory.open( new File( indexPath ) );
140.
Analyzer analyzer = new StandardAnalyzer( Version.LUCENE_4_9 );
141.
IndexWriterConfig iwc = new IndexWriterConfig(
Version.LUCENE_4_9, analyzer );
142.
iwc.setOpenMode( ( createNew ) ? OpenMode.CREATE :
OpenMode.CREATE_OR_APPEND );
143.
IndexWriter writer = new IndexWriter( dir, iwc );
144.
List<File> filesIndexed = indexDocs( writer, docDir,
Charset.forName( charEncoding ) );
145.
writer.close();
146.
analyzer.close();
147.
dir.close();
148.
return filesIndexed;
149.
}
150.
151.
private static List<File> indexDocs( IndexWriter writer, File file,
Charset charEnc ) throws IOException
152.
{
153.
List<File> filesIndexed = new ArrayList<File>();
154.
if( file.canRead() ) {
155.
if( file.isDirectory() ) {
156.
String[] files = file.list();
157.
if( files != null ) {
158.
for( int i = 0; i < files.length; i++ ) {
159.
filesIndexed.addAll( indexDocs( writer, new File(
file, files[i] ), charEnc ) );
160.
}
161.
}
162.
} else {
163.
BufferedReader fileReader;
164.
try {
165.
fileReader = new BufferedReader( new InputStreamReader(
new FileInputStream( file ), charEnc ) );
166.
} catch( FileNotFoundException fnfe ) {
167.
return filesIndexed;
168.
}
169.
try {
170.
Document doc = new Document();
171.
Field pathField = new StringField( "path",
file.getPath(), Field.Store.YES );
172.
doc.add( pathField );
173.
doc.add( new LongField( "modified",
file.lastModified(), Field.Store.NO ) );
174.
doc.add( new TextField( "contents", fileReader ) );
175.
if( writer.getConfig().getOpenMode() == OpenMode.CREATE
) {
176.
writer.addDocument( doc );
177.
} else {
178.
writer.updateDocument( new Term( "path",
file.getPath() ), doc );
179.
}
180.
filesIndexed.add( file );
181.
} finally {
182.
fileReader.close();
183.
}
184.
}
185.
}
186.
return filesIndexed;
187.
}
188. }
189.
190. Sehen Sie sich hierzu die Lucene-API-Doku an:
Lucene core API.
191.
192. Erzeugen Sie im src\test\java\de\meinefirma\meinprojekt-Verzeichnis
folgende Modultest-Klasse: LuceneSimpleUtilTest.java
193.
194.
195. package de.meinefirma.meinprojekt;
196.
197. import static org.junit.Assert.*;
198. import java.io.File;
199. import java.nio.charset.StandardCharsets;
200. import java.util.List;
201. import org.apache.lucene.document.Document;
202. import org.junit.Test;
203.
204. public class LuceneSimpleUtilTest
205. {
206.
@Test
207.
public void testLuceneSearch() throws Exception
208.
{
209.
String docsPath = "src";
210.
String indexPath = "target/LuceneIndex";
211.
String charEnc
= StandardCharsets.UTF_8.toString();
212.
String suchWort = "Lucene";
213.
214.
System.out.println( "Indexerstellung im Verzeichnis:
" +
indexPath );
215.
System.out.println( "Zu indizierende Dateien im Verzeichnis: " +
docsPath );
216.
System.out.println( "Character-Encoding:
" +
charEnc
);
217.
System.out.println( "Indizierte Dateien:" );
218.
List<File> filesIndexed = LuceneSimpleUtil.indexFiles( docsPath,
charEnc, indexPath, true );
219.
System.out.println( filesIndexed );
220.
assertNotNull( filesIndexed );
221.
assertEquals( 2, filesIndexed.size() );
222.
223.
System.out.println( "Suchwort '" + suchWort + "' gefunden in:"
);
224.
List<Document> docs = LuceneSimpleUtil.searchFiles( indexPath,
suchWort, 100 );
225.
for( Document doc : docs ) {
226.
System.out.println( doc.get( "path" ) );
227.
}
228.
assertEquals( 1, docs.size() );
229.
}
230. }
231.
232.
233. Ihre Projektverzeichnisstruktur sieht jetzt so aus:
234.
tree /F
[\MeinWorkspace\MvnLucene]
|- [src]
|
|- [main]
|
|
'- [java]
|
|
'- [de]
|
|
'- [meinefirma]
|
|
'- [meinprojekt]
|
|
'- LuceneSimpleUtil.java
|
'- [test]
|
'- [java]
|
'- [de]
|
'- [meinefirma]
|
'- [meinprojekt]
|
'- LuceneSimpleUtilTest.java
'- pom.xml
235.
236. In dem JUnit-Test LuceneSimpleUtilTest wird gezeigt, wie Sie Lucene
programmatisch ansprechen können.
237.
Geben Sie im Kommandozeilenfenster Folgendes ein:
238.
cd \MeinWorkspace\MvnLucene
mvn test
Sie erhalten:
Running de.meinefirma.meinprojekt.LuceneSimpleUtilTest
Indexerstellung im Verzeichnis:
target/LuceneIndex
Zu indizierende Dateien im Verzeichnis: src
Character-Encoding:
UTF-8
Indizierte Dateien:
[src\main\java\de\meinefirma\meinprojekt\LuceneSimpleUtil.java,
src\test\java\de\meinefirma\meinprojekt\LuceneSimpleUtilTest.java]
Suchwort 'Lucene' gefunden in:
src\test\java\de\meinefirma\meinprojekt\LuceneSimpleUtilTest.java
239.
240. Sie können LuceneSimpleUtil auch per Kommandozeilenfenster aufrufen.
241.
Im folgenden Beispiel wird wieder in allen Dateien in allen
Verzeichnissen unter src nach Lucene gesucht:
242.
cd \MeinWorkspace\MvnLucene
mvn package
java -jar target\MvnLucene-1.0-SNAPSHOT-jar-with-dependencies.jar
-->
Indizieren mit 3 Parametern:
PfadZuDokumenten CharacterEncoding PfadZumIndex
Suchen mit 2 Parametern:
PfadZumIndex SuchWort
Indizierung durch Angabe von "PfadZuDokumenten CharacterEncoding
PfadZumIndex":
java -jar target\MvnLucene-1.0-SNAPSHOT-jar-with-dependencies.jar src
ISO-8859-1 LuceneIndex
-->
Indexerstellung im Verzeichnis:
LuceneIndex
Zu indizierende Dateien im Verzeichnis: src
Character-Encoding:
ISO-8859-1
Indizierte Dateien:
[src\main\java\de\meinefirma\meinprojekt\LuceneSimpleUtil.java,
src\test\java\de\meinefirma\meinprojekt\LuceneSimpleUtilTest.java]
Suchen durch Angabe von "PfadZumIndex SuchWort":
java -jar target\MvnLucene-1.0-SNAPSHOT-jar-with-dependencies.jar
LuceneIndex Lucene
-->
Index im Verzeichnis: LuceneIndex
Suchwort 'Lucene' gefunden in:
src\test\java\de\meinefirma\meinprojekt\LuceneSimpleUtilTest.java
243.
244. Indizieren Sie statt in src alle Dateien in einem anderen Verzeichnis
und testen Sie andere Suchwörter.
245.
Java-EE-Anwendungen (Servlet, JSP, JSF, JPA, EJB3)
Maven-Projekte für die Java Enterprise Edition finden Sie zum Beispiel
unter:

Webanwendung mit Servlet, JSP, JavaBean und DB-Zugriff für GlassFish,
WebLogic, JBoss und Geronimo

Webanwendung mit JSF und JPA für GlassFish

Java EE mit EJB3 für GlassFish, WebLogic, JBoss und Geronimo
Maven mit Docker
Beispiele zur Verwendung von
Docker
mit Maven finden Sie unter:

Docker-Image mit Maven erzeugen mit dem spotify docker-maven-plugin

Docker mit Maven steuern mit dem rhuss docker-maven-plugin
Maven-Repository
Zugang zu Internet-Repositories über Firmen-Proxy
In Firmennetzwerken ist häufig der direkte Internetzugang für Tools wie
Maven gesperrt.
Um dennoch benötigte Libs aus den Maven-Repositories im Internet
verwenden zu können, kann der
Cntlm Authentication Proxy verwendet werden
(wenn die IT-Abteilung das erlaubt).
Er steht für verschiedene Betriebssysteme zur Verfügung.
Hier folgt eine Kurzanleitung für Windows-Workstations, die übers
Firmennetzwerk nur über eine NTLM-Authentifizierung ins Internet gelangen.
Installieren Sie Cntlm wie beschrieben unter
Eclipse-Updates über Firmen-Firewalls mit dem Cntlm Authentication Proxy
(die Eclipse-spezifischen Einstellungen können Sie weglassen).
Erweitern Sie Ihre lokale
%M2_HOME%\conf\settings.xml
zum Beispiel so (passen Sie den Pfad an und setzen Sie als Portnummer
dieselbe wie bei Cntlm ein):
<?xml version="1.0" encoding="UTF-8"?>
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0
http://maven.apache.org/xsd/settings-1.0.0.xsd">
<localRepository>D:\Tools\Maven3-Repo</localRepository>
<proxies>
<proxy>
<active>true</active>
<protocol>http</protocol>
<host>127.0.0.1</host>
<port>3128</port>
</proxy>
</proxies>
</settings>
Team-Repository über freigegebenes Verzeichnis
Wenn nur ein festes Repertoire an Libs mehreren Entwicklern zur Verfügung
gestellt werden soll,
können Sie ein Maven-Repository als einfaches Verzeichnis einstellen.
Allerdings werden so keine fehlenden Libs übers Internet nachgeladen.
Erweitern Sie Ihre lokale
settings.xml
zum Beispiel so:
<?xml version="1.0" encoding="UTF-8"?>
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0
http://maven.apache.org/xsd/settings-1.0.0.xsd">
<localRepository>D:\Tools\Maven3-Repo</localRepository>
<mirrors>
<mirror>
<id>MeineMirrorId</id>
<mirrorOf>central</mirrorOf>
<url>file://\\<MeinPC>\<MeinFreigabeName>\m2-Central-Repository</url>
</mirror>
</mirrors>
</settings>
Repository-Manager
Falls ein Repository-Manager (im Beispiel: Archiva)
installiert ist, können Sie ihn über folgenden Eintrag in der
settings.xml
einstellen:
<?xml version="1.0" encoding="UTF-8"?>
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0
http://maven.apache.org/xsd/settings-1.0.0.xsd">
<localRepository>D:\Tools\Maven3-Repo</localRepository>
<mirrors>
<mirror>
<id>archiva</id>
<mirrorOf>*</mirrorOf>
<url>http://<ArchivaServer>:<ArchivaPort>/archiva/repository/internal</url>
</mirror>
</mirrors>
</settings>
Installation und Konfiguration des Repository-Managers Archiva
Archiva ist einer der bekanntesten Repository-Manager.
Installieren Sie ihn wie beschrieben unter
System Administrators Guide to Apache Archiva.
Einige eventuell benötigte Schritte sind auch im Folgenden aufgeführt.
1. Downloaden Sie apache-archiva-1.3.4-bin.zip von
2.
http://archiva.apache.org/download.cgi
3.
und entzippen Sie die Datei, zum Beispiel nach
4.
\Tools\Archiva.
5. Suchen Sie in
6.
\Tools\Archiva\conf\jetty.xml
7.
nach "jetty.port" und setzen Sie bei default="8080" eine andere
Portnummer ein, beispielsweise "4422".
8.
9.
(Freie Portnummern finden Sie unter
http://www.iana.org/assignments/port-numbers.)
10.
11.
12.
13.
Falls Sie unter 64-bit-Windows installieren:
Der Archiva-Wrapper funktioniert zurzeit nur mit einem 32-bit-JDK.
Dieses muss installiert (z.B. nach C:\Program Files
(x86)\Java\jdk1.7-32bit) und bei Archiva eingetragen werden.
14.
Hierzu in der
15.
\Tools\Archiva\conf\wrapper.conf
16.
die Zeile
17.
wrapper.java.command=java
ändern zu:
wrapper.java.command=C:\Program Files (x86)\Java\jdk1.7-32bit\bin\java
Außerdem können einige Archiva-Kommandos (z.B. "bin\archiva.bat
install") nur in einem Kommandozeilenfenster
mit Administratorrechten ausgeführt werden:
Start | Alle Programme | Zubehör | "Eingabeaufforderung" mit
rechter Maustaste und "Als Administrator ausführen".
18.
19.
Starten Sie Archiva über:
\Tools\Archiva\bin\archiva.bat console
Wenn "Started [email protected]:4422" erscheint, starten
Sie die Archiva-Weboberfläche:
start http://localhost:4422/archiva
20. Legen Sie einen Admin-Account an.
21. Falls Sie in einem Firmennetzwerk hinter einem Proxy sitzen und den
22.
Cntlm Authentication Proxy installiert haben, legen Sie in Archiva
einen Network Proxy an.
23.
Klicken Sie in der linken Spalte unter "Administration" auf
24.
"Network Proxies"
25.
und dann auf "Add Network Proxy" und tragen Sie ein:
26.
Identifier: MeineNetworkProxyID
Protocol:
Hostname:
Port:
Username:
Password:
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
http
127.0.0.1
3128
Save Network Proxy.
In der
\Tools\Archiva\conf\archiva.xml
entsteht der Eintrag:
<networkProxies>
<networkProxy>
<id>MeineNetworkProxyID</id>
<host>127.0.0.1</host>
<port>3128</port>
<username/>
<password/>
</networkProxy>
</networkProxies>
44. Tragen Sie den Network Proxy in alle Proxy Connectors ein.
45.
Klicken Sie in der linken Spalte auf
46.
"Proxy Connectors"
47.
und tragen Sie dann bei allen Proxy Connectoren über "Edit Proxy
Connector" ein:
48.
Network Proxy: MeineNetworkProxyID
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
Save Proxy Connector.
In der
\Tools\Archiva\conf\archiva.xml
entstehen die Einträge:
<proxyConnector>
...
<proxyId>MeineNetworkProxyID</proxyId>
...
</proxyConnector>
61. Fügen Sie folgendes Repository hinzu.
62.
63.
64.
Klicken Sie in der linken Spalte auf
"Repositories"
und dann rechts unter "Remote Repositories" auf "Add" und tragen
Sie ein:
65.
Identifier: maven1-repository.dev.java.net
Name:
URL:
Type:
66.
67.
Java.net Repository for Maven 1
http://download.java.net/maven/1/
Maven 1.x Repository
Add Repository.
68. Fügen Sie einen Proxy Connector für das "Java.net Repository for Maven
1" hinzu:
69.
Klicken Sie in der linken Spalte auf
70.
"Proxy Connectors"
71.
und dann auf "Add" und tragen Sie ein:
72.
MeineNetworkProxyID
Managed Repository: internal
Remote Repository: maven1-repository.dev.java.net
yes
Cache failures:
once
Releases:
fix
Checksum:
never
Snapshots:
Network Proxy:
73.
74.
Add Proxy Connector.
75. Erweitern Sie auf dem Entwickler-PC die lokale
76.
%M2_HOME%\conf\settings.xml
77.
wie oben beschrieben und testen Sie,
78.
ob Sie mit dem Archiva Repository ihre Maven-Projekte bauen
können.
79. Stoppen Sie Archiva mit "Strg+C".
80. Installieren Sie Archiva als Hintergrund-Dienst:
81.
\Tools\Archiva\bin\archiva.bat install
Wenn Sie jetzt Windows neu starten, läuft Archiva automatisch als
Dienst.
Steuern können Sie dies unter:
"Start" | "Systemsteuerung" | ["System und Sicherheit"] |
"Verwaltung" | "Dienste"
(oder alternativ: rechter Mausklick auf "Computer" bzw.
"Arbeitsplatz" | "Verwalten" | "Dienste und Anwendungen" | "Dienste").
Für kleine Teams in Firmen kann praktisch sein, dass der so
installierte Archiva-Dienst bereits funktionsfähig
und von anderen PCs aus erreichbar läuft, ohne dass sich jemand
auf dem Archiva-Server im Firmennetzwerk anmelden muss.
82.
83.
Falls Sie bei korrekt installiertem Archiva-Dienst nur den ArchivaDienst stoppen oder starten wollen, können Sie das net-Kommando
verwenden:
84.
net stop archiva
net start archiva
Distribution-Deployment mit dem Repository-Manager Archiva
Mit
mvn install
kopieren Sie Ihre Ergebnisartefakte in Ihr lokales Maven-Repository.
Falls Sie im Team arbeiten und Ihre Kollegen Ihre Ergebnisartefakte
verwenden können sollen,
müssen Sie diese mit
mvn deploy
in ein gemeinsam genutzes Remote Repository kopieren.
Dies kann ein projektspezifisches, teamspezifisches, firmenspezifisches
oder ein übers Internet erreichbares sein.
Im Folgenden werden die dafür benötigten Einstellungen am Beispiel des
Archiva-Repository-Managers aufgeführt.
1.
2.
3.
4.
5.
Archiva einrichten wie oben beschrieben.
Der Archiva-Administrator legt in Archiva die Benutzer für das
Distribution-Deployment an (im Folgenden "<DeployerName>" genannt):
6.
7.
1. http://<ArchivaServer>:<ArchivaPort>/archiva
2.
(z.B. http://localhost:4422/archiva) starten.
8.
1. Als admin anmelden und "User Management" wählen.
9.
1. "Neuen Benutzer anlegen" klicken und Felder ausfüllen.
10.
1. Unter "Resource Roles": Alle vier Checkboxen für "Repository
Manager" und "Repository Observer" sowie "internal" und
"snapshots" aktivieren.
11.
12.
13.
14.
Der Entwickler/Deployer ändert das Kennwort und kontrolliert den
Archiva-Account:
15.
16.
1. http://<ArchivaServer>:<ArchivaPort>/archiva öffnen.
17.
1. Falls bereits angemeldet (z.B. als admin): abmelden.
18.
1. Als <DeployerName> anmelden.
19.
1. Kennwort ändern.
20.
21.
22.
23.
24.
25.
Auf dem Deployer-PC die
%M2_HOME%\conf\settings.xml
erweitern (dabei korrekte Archiva-URL und korrekten DeployerBenutzer eintragen):
26.
27.
28. <?xml version="1.0" encoding="UTF-8"?>
29. <settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
30.
xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0
http://maven.apache.org/xsd/settings-1.0.0.xsd">
31.
32.
<localRepository>D:\Tools\Maven3-Repo</localRepository>
33.
34.
<mirrors>
35.
<mirror>
36.
<id>archiva</id>
37.
<mirrorOf>*</mirrorOf>
38.
<url>http://<ArchivaServer>:<ArchivaPort>/archiva/repository/internal</
url>
39.
</mirror>
40.
</mirrors>
41.
42.
<servers>
43.
<server>
44.
<id>archiva</id>
45.
<username>..._DeployerName_...</username>
46.
<password>....................</password>
47.
</server>
48.
</servers>
49.
50. </settings>
51.
52.
53.
54.
Auf dem Deployer-PC ein Testprojekt anlegen:
cd \MeinWorkspace
mvn archetype:generate -DinteractiveMode=false -DgroupId=test1 DartifactId=Test1
cd Test1
55.
56.
57.
58.
59.
60.
61.
62.
Auf dem Deployer-PC für den Test die pom.xml des Test1-Projekts
erweitern (dabei korrekte Archiva-URL eintragen):
<distributionManagement>
<repository>
<id>archiva</id>
<url>http://<ArchivaServer>:<ArchivaPort>/archiva/repository/internal</
url>
63.
</repository>
64.
<snapshotRepository>
65.
<id>archiva</id>
66.
<url>http://<ArchivaServer>:<ArchivaPort>/archiva/repository/snapshots<
/url>
67.
</snapshotRepository>
68.
</distributionManagement>
69.
Diese POM-Einstellung erfolgt am besten in einer "Corporate POM (oder
Projekt-Master-POM)".
70.
71.
72.
Snapshot-Deployment:
cd \MeinWorkspace\Test1
mvn deploy
73.
74.
75.
Release-Deployment:
In der pom.xml die Zeile
<version>1.0-SNAPSHOT</version>
ändern zu:
<version>1.0</version>
und aufrufen:
mvn deploy
76.
77.
78.
79.
80.
81.
82.
83.
Kontrolle:
http://<ArchivaServer>:<ArchivaPort>/archiva/browse
aufrufen und beide deployte Artefakte überprüfen.
Doku zum Maven-Deploy-Plugin:
84.
85.
86.
87.
88.
89.
90.
91.
92.
http://maven.apache.org/plugins/maven-deploy-plugin und
http://maven.apache.org/plugins/maven-deploy-plugin/usage.html
Sehen Sie sich auch die Hinweise zum
Release-Auslieferungsprozess an.
Repository-Manager Nexus
Alternativ zu Archiva können Sie auch Nexus verwenden:
1.
2.
3.
4.
Sehen Sie sich die Nexus-Referenzdoku an.
5.
6.
7.
Downloaden Sie Nexus OSS
(als Zipdatei, z.B. nexus-2.10.0-02-bundle.zip) und entzippen Sie
das Archiv, z.B. nach
8.
\Tools\Nexus.
9.
10.
11.
12.
Der Nexus-Default-Port ist 8081.
Da dieser Port auch von vielen anderen Anwendungen bevorzugt wird,
sollten Sie einen anderen Port einstellen,
13.
beispielsweise 4422. Tragen Sie die gewünschte Portnummer bei
application-port=... in der
14.
\Tools\Nexus\conf\nexus.properties
15.
ein.
16.
17.
18.
19.
20.
21.
22.
23.
Öffnen Sie ein Kommandozeilenfenster mit Admin-Rechten
und verzweigen Sie in das Nexus-bin-Verzeichnis, z.B.:
\Tools\Nexus\bin.
Um Nexus für einen ersten Test zu starten, führen Sie aus
(und warten, bis "Started [email protected]:4422"
oder "Started [email protected]:4422"
erscheint):
cd \Tools\Nexus\bin
nexus.bat console
24.
25.
26.
27.
Um Nexus als Windows-Dienst
zu installieren und automatisch zu starten, führen Sie aus:
nexus.bat install
net start nexus
Überprüfen Sie die Parameter in der Windows-Registry unter:
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\nexus-webapp.
28.
29.
30.
31.
Starten Sie Nexus und sehen Sie sich die Admin-Konsole an:
Starten Sie http://localhost:4422/nexus,
klicken Sie oben rechts auf "Log In" und geben Sie als Username
"admin" und als Password "admin123" ein.
32.
33.
34.
35.
36.
Überprüfen Sie die Einstellungen, insbesondere unter
"Views/Repositories" | "Repositories" sowie
"Administration" | "Server".
Beachten Sie, dass Sie je nach Nexus-Konfiguration nicht alle
Menüeinträge und Parameter sehen können,
37.
wenn Sie sich remote über
38.
http://NexusPcName:4422/nexus verbinden.
39.
In diesem Fall müssen Sie Nexus direkt lokal auf dem Nexus-PC mit
localhost aufrufen, also über
http://localhost:4422/nexus.
40.
41.
Falls Ihr Netzwerk nur über einen Proxy ins Internet gelangt (wie es
in Firmen häufig der Fall ist),
42.
sehen Sie sich obige Bemerkungen zum Firmen-Proxy an.
43.
44.
Auf der soeben erwähnten Nexus-Admin-Seite können Sie unter
"Administration" | "Server" | "Default HTTP Proxy Settings
(optional)"
45.
den Proxy-Host und -Port eintragen.
46.
47.
48.
49.
50.
Falls Sie über
"Views/Repositories" | "Repositories" | "Add..."
ein neues Repository hinzufügen: Vergessen Sie nicht, das neu
angelegte Repository auch der Gruppe "Public Repositories"
hinzuzufügen.
51.
52.
53.
Sie sollten für alle Repositories konfigurieren:
54.
File Content Validation = True
55.
Checksum Policy = StrictIfExists
56.
Dies ist vor allem dann wichtig, wenn Sie sich in einem
Firmennetzwerk hinter einem Proxy befinden, siehe
57.
http://books.sonatype.com/nexus-book/reference/confignx-sectmanage-repo.html.
58.
Solche Proxys antworten häufig beim Download mit
zwischengeschalteten HTML-Seiten.
59.
Falls Sie beim Build auf "unerklärliche Fehler" (z.B.
ClassNotFoundException oder NoClassDefFoundError) stoßen:
60.
Überprüfen Sie im Nexus-Datenverzeichnis alle vermeintlichen
*.jar-Dateien, ob sie in Wirklichkeit nur HTML-Inhalt haben.
61.
Falls Sie eine solche fehlerhafte *.jar-Datei finden: Löschen Sie
das gesamte Verzeichnis dieser Datei und führen Sie "Repair Index" aus.
62.
Falls es keine andere Möglichkeit gibt, als eine .jar-Datei
manuell downzuloaden und manuell der Nexus-Datenbank hinzuzufügen:
63.
Denken Sie daran, zusätzlich auch die .jar.sha1-Datei hinzuzufügen
und anschließend Nexus neu zu starten.
64.
Außerdem müssen Sie eventuell beim Client-PC in dessen lokalem
Maven-Repository alle Verzeichnisse löschen,
65.
welche auf *.lastUpdated endende Dateien enthalten.
66.
67.
68.
Auf den Workstations, auf denen Maven den Nexus-Repository-Server
verwenden soll, muss jeweils in der
69.
%M2_HOME%\conf\settings.xml
70.
der Inhalt des <mirrors>-Tags ersetzt werden durch
71.
(siehe auch Configuring Maven):
72.
73.
74.
<mirror>
75.
<id>nexus</id>
76.
<mirrorOf>*</mirrorOf>
77.
<url>http://__NEXUS_SERVER__:__NEXUS_PORT__/nexus/content/groups/public
</url>
78.
</mirror>
79.
80.
<!-- Wird fuer Distribution-Deployment ins Remote-Maven-Repository
benoetigt
81.
(siehe "<distributionManagement>" in Master-POM)
82.
(nur einfuegen, wenn Account wirklich existiert): -->
83.
<servers>
84.
<server>
85.
<id>nexus</id>
86.
<username>__MEIN_NEXUS_BENUTZERNAME__</username>
87.
<password>__MEIN_NEXUS_KENNWORT__</password>
88.
</server>
89.
</servers>
90.
Dabei "__NEXUS_SERVER__:__NEXUS_PORT__" durch korrekte Werte ersetzen,
zum Beispiel "localhost:4422".
91.
92.
93.
94.
Falls Sie eigene Artefakte
(z.B. JDBC-Treiber oder JPA-Provider)
95.
ins Nexus-Repository deployen wollen, loggen Sie sich als Admin
ein und wählen Sie:
96.
links Menüpunkt: "Repositories" | Mitte oben Zeile: "Releases" |
Mitte unten Reiter: "Artifact Upload" | ...
97.
98.
99.
Falls Sie folgende Fehlermeldung erhalten:
100.
"[ERROR] Plugin ... or one of its dependencies could not be
resolved: Failed to read artifact descriptor for ...: Could not
transfer artifact ... from/to nexus: Not authorized,
ReasonPhrase:Unauthorized:
101.
Dann überprüfen Sie sehr genau die <server>-Account-Daten.
102.
103.
104. Falls Sie folgende Fehlermeldung erhalten:
105.
"[ERROR] Failed to execute goal on project ...: Could not resolve
dependencies for project ...: Failure to find ... in ... was cached in
the local repository, resolution will not be reattempted until the
update interval of nexus has elapsed or updates are forced -> [Help
1]",
106.
und falls Sie die Wartezeit nicht abwarten wollen:
107.
Löschen Sie vor einem neuen Versuch im lokalen Maven-Repository
alle Verzeichnisse, welche auf *.lastUpdated endende Dateien enthalten.
108.
109.
110. Falls Sie folgende Fehlermeldung erhalten:
111.
"Unable to update index for Nexus ...":
112.
In Nexus als admin einloggen und
113.
entweder unter Views/Repositories | Repositories mit der rechten
Maustaste auf Public Repositories und "Repair Index" klicken
114.
oder unter Administration | Scheduled Tasks die Task "Repair
Repositories Index" einrichten und ausführen.
115.
Falls Sie Eclipse und das
116.
M2Eclipse-Plugin
117.
installiert haben, müssen Sie eventuell zusätzlich Eclipse
schließen und den Cache unter
118.
%M2_REPO%\.cache\m2e
119.
(z.B. %USERPROFILE%\.m2\repository\.cache\m2e
120.
oder
121.
\Tools\Maven3-Repo\.cache\m2e)
122.
löschen.
123.
124.
125. Falls Sie beim Start des Nexus-Servers als Windows-Dienst diese
Fehlermeldung erhalten:
126.
Der Dienst ... konnte nicht gestartet werden. Fehler 1067: Der Prozess
wurde unerwartet beendet.
und in der
\Tools\Nexus\logs\wrapper.log
steht:
wrapper | Unable to execute Java command.
angegebene Datei nicht finden. (0x2)
Das System kann die
wrapper
|
"java" -... ...
wrapper
| Critical error: wait for JVM process failed
Dann ersetzen Sie in der
\Tools\Nexus\bin\jsw\conf\wrapper.conf
die Zeile
wrapper.java.command=java
durch
wrapper.java.command=C:\Program Files\Java\jdk1.7\bin\java
Continuous Integration mit Jenkins / Hudson und Maven 3
Continuous Integration mit
Jenkins oder
Hudson
ermöglicht die kontinuierliche Überwachung, ob Systeme funktionsfähig
sind, sowie ob die ins Versionskontrollsystem
(= "VCS", = "SCM", = Source Code Management, z.B. Git, Mercurial,
Subversion, CVS)
eingecheckten Module kompilierfähig sind, zusammenpassen und ob alle
Tests (auch Integrationstests) fehlerfrei durchlaufen werden.
Im Folgenden werden einige wichtige Schritte kurz beschrieben, um Jenkins
für Maven-3-Projekte einzurichten.
Die meisten Hinweise gelten genau so auch für Hudson.
1.
2.
Die folgende Beschreibung geht von einem installierten Maven 3 und von
bereits vorhanden und ins VCS/SCM eingecheckten Modulen aus.
3.
4.
5.
Jenkins legt seine Arbeitsdateien defaultmäßig in einem .jenkinsUnterverzeichnis im
6.
<user-home>-Verzeichnis (z.B.
7.
C:\Users\%USERNAME%) ab.
8.
Zumindest in Firmen sollten Sie dies vermeiden,
9.
weil häufig das <user-home>-Verzeichnis bei jedem PC-Herunterfahren
gesichert
10.
und beim Starten wiederhergestellt wird, was unnötig BackupSpeicherplatz kosten und diese Vorgänge verlangsamen würde.
11.
Um dies zu vermeiden, erzeugen Sie ein neues Verzeichnis und übergeben
dies als Umgebungsvariable, zum Beispiel so:
md C:\Tools\Jenkins
set JENKINS_HOME=C:\Tools\Jenkins
12.
13.
Downloaden Sie die Jenkins-Installations-WAR-Datei (z.B. jenkins1.556.war) von
14.
http://jenkins-ci.org,
15.
zum Beispiel in das bei %JENKINS_HOME% definierte Verzeichnis
(z.B. C:\Tools\Jenkins).
16.
17.
18.
Um zukünftige Updates zu erleichtern und um die mitgelieferten
Skripte verwenden zu können,
19.
sollten Sie die Versionsnummer aus dem Dateinamen entfernen.
20.
Umbenennen Sie die downgeloadete Datei in jenkins.war.
21.
22.
23.
24.
Prüfen Sie, ob die gewünschte Portnummer (z.B. 4424) noch frei ist:
netstat -a
netstat -an | find ":4424"
25.
26.
Wechseln Sie in das Verzeichnis mit der jenkins.war-Datei und rufen
Sie auf
27.
(eventuell statt mit 4424 mit anderer Portnummer):
28.
cd C:\Tools\Jenkins
start "Jenkins" java -jar jenkins.war --httpPort=4424
Warten Sie, bis "Completed initialization" erscheint.
start http://localhost:4424
29.
30.
Wählen Sie im Jenkins-Hauptmenü "Jenkins verwalten" | "System
konfigurieren":
31.
32.

Überprüfen Sie, ob das "Jenkins Home-Verzeichnis" korrekt ist
(z.B. C:\Tools\Jenkins).

Stellen Sie unter "Anzahl Build-Prozessoren" ein, in wie vielen
parallelen Threads die Jobs abgearbeitet werden sollen.
Falls Sie keine Parallelität wollen, stellen Sie 1 ein.





Unter "Maven Installationen":
"Name" vergeben (z.B. "Maven 3").
"Automatisch installieren" deaktivieren.
"MAVEN_HOME" eingeben (z.B. D:\Tools\Maven3).

Falls Sie CVS verwenden und sich das Verzeichnis zur cvs.exe
nicht im PATH befindet,
geben Sie unter "CVS"/"CVS Befehl" den vollständigen Pfad
zur cvs.exe ein
(z.B. unter 64-bit-Windows: C:\Program Files
(x86)\cvsnt\cvs.exe).


33.
34.
35.

"E-Mail Benachrichtigung" ausfüllen falls gewünscht.

"Jenkins URL" anpassen.

"Übernehmen".
Wählen Sie im Jenkins-Hauptmenü "Neuen Job anlegen":
36.

"Job Name" eintragen (z.B. identisch zum Projektmodulnamen).

Beachten: Für Maven-3-Projekte funktioniert nicht: "Maven 2
Projekt bauen".

Falls es keine Auswahlmöglichkeit für Maven-3-Projekte gibt
(wie z.B. bei Hudson 1.384), wählen:

"Free Style"-Softwareprojekt bauen.

"OK"



"Source-Code-Management":
Z.B. "CVS":
"CVSROOT" (z.B.
:pserver:<Benutzername>@<CvsServerUrl>:<Pfad>),
"Module": Projektmodulname.



"Build-Auslöser":
Z.B. "Builds zeitgesteuert starten", "Zeitplan" z.B. "11 *
* * *".


"Buildverfahren":
"Build-Schritt hinzufügen", "Maven Goals aufrufen", bei
"Maven Version" den oben eingetragen Namen (z.B. "Maven 3"),
"Goals" z.B. "clean install site".



Statt "Maven Goals aufrufen" können Sie unter Windows auch
"Windows-Batchdatei" wählen um Kommandozeilenbefehle ausführen.
Zum Beispiel können Sie Ergebnisse kopieren, etwa so:


xcopy "%WORKSPACE%\target\site"
"\Tools\Tomcat\webapps\ROOT\sitereport\%JOB_NAME%\site\" /S
Oder Sie überprüfen mit cURL REST-Webservices, etwa so:

curl -s
"http://meinrestserver:8080/rest/meinservice?parm1=7&parm2=Xyz" |
find "Ergebnis=42"



"Post-Build-Aktionen":
Eventuell "Javadoc veröffentlichen",
"Veröffentliche JUnit-Testergebnisse" mit "Testberichte in
XML-Format: target/surefire-reports/*.xml".

"Übernehmen".
37.
38.
39.
40.
41.
Jenkins setzt einige
Jenkins Environment Variables,
die Sie während der Jenkins-Jobs verwenden können.
Einige der Werte können innerhalb der Jenkins-Job-Beschreibungen
hilfreich sein,
42.
zum Beispiel um Kopierkommandos zu formulieren (z.B. WORKSPACE und
JOB_NAME, s.o.).
43.
Andere Werte können auch zur Laufzeit interessant sein (z.B.
BUILD_ID, BUILD_NUMBER und CVS_BRANCH).
44.
Sie können sie während des Buildprozesses automatisch in
45.
Properties-Dateien oder in die
46.
MANIFEST.MF eintragen.
47.
48.
49.
Die bereits im Betriebssystem konfigurierten Environment Variablen
(Umgebungsvariablen)
50.
stehen innerhalb der Jenkins-Jobs nicht so ohne weiteres zur
Verfügung.
51.
Wenn Sie bestimmte Umgebungsvariablen benötigen, beispielsweise
NLS_LANG für Oracle SqlPlus,
52.
dann müssen Sie diese Umgebungsvariablen entweder innerhalb
Jenkins global setzen
53.
(Jenkins verwalten | System konfigurieren | Globale Eigenschaften
| Umgebungsvariablen)
54.
oder innerhalb der einzelnen Jenkins-Jobs setzen
55.
(notfalls z.B. per "set NLS_LANG=German_Germany.AL32UTF8").
56.
Alternativ können Sie Umgebungsvariablen auch mit speziellen
Jenkins-Plugins konfigurieren.
57.
58.
59.
Jenkins sorgt normalerweise dafür, dass alle vom Jenkins-Job
gestarteten Prozesse automatisch am Ende des Jenkins-Jobs beendet
werden.
60.
Dies ist normalerweise das gewünschte Verhalten.
61.
Falls Sie in einem Jenkins-Job Prozesse starten wollen, die nicht
von Jenkins beendet werden sollen,
62.
können Sie dies erreichen, indem Sie in diesem Job die
Umgebungsvariable BUILD_ID setzen, zum Beispiel so:
63.
"set BUILD_ID=dontKillMe". Siehe hierzu:
64.
https://wiki.jenkins-ci.org/display/JENKINS/ProcessTreeKiller.
65.
Falls Sie den von Jenkins gesetzten Inhalt der
66.
BUILD_ID
67.
während des Jobs verwenden wollen, müssen Sie die BUILD_ID vorher
in eine andere Umgebungsvariable retten.
68.
69.
70.
71.
72.
Falls Sie CVS verwenden:
Ein CVS-Kommandozeilenclient muss installiert sein, zum Beispiel per
cvsnt_setup.exe aus WinCvs2_0_2-4.zip
(der cvsnt-Server-Teil kann während der Installation deaktiviert
werden und WinCvs braucht nicht installiert werden).
Falls Sie folgende Fehlermeldung erhalten:
"cvs checkout: Empty password used - try 'cvs login' with a real
password":
Dann führen Sie (unter dem richtigen Benutzer) einmalig einen Login
ins CVS per Kommandozeile durch, z.B. so:
cvs.exe -d:pserver:<Benutzername>@<CvsServerUrl>:<Pfad> login
Dabei entsteht entweder im C:\Users\%USERNAME%-Verzeichnis
die Passwortdatei .cvspass oder in der Windows-Registry ein
cvspass-Eintrag entweder
unter HKEY_CURRENT_USER\Software\cvsnt oder
unter HKEY_USERS\.DEFAULT\Software\cvsnt.
Falls Sie von Jenkins aus SonarQube-Reports erstellen wollen, Maven 3
verwenden und Schwierigkeiten mit dem SonarQube-Plugin
(https://wiki.jenkins-ci.org/display/JENKINS/SonarQube+plugin)
haben, dann können Sie SonarQube auch leicht ohne das SonarQube-Plugin in
Jenkins einbinden:
1.
2.
3.
4.
5.
6.
7.
8.
Installieren und starten Sie den SonarQube-Server, wie oben unter
Sourcecodeanalyse mit SonarQube
beschrieben ist (am besten als automatisch startender Dienst).
Klicken Sie im Jenkins-Hauptmenü auf den gewünschten "Job" und wählen
Sie "Konfigurieren":
"Buildverfahren", "Build-Schritt hinzufügen", "Maven Goals
aufrufen",
9.
bei "Maven Version" den oben eingetragen Namen (z.B. "Maven 3"),
10.
"Goals" "clean install sonar:sonar".
11.
Falls Sie viele Jenkins-Jobs haben, kann es sinnvoll sein, mit dem "Nested
View Plugin" zusätzliche Gliederungsebenen einzuführen.
Dabei wird in der summarischen Ansicht angezeigt, ob es innerhalb der
Ordner Fehlschläge gibt, oder ob alles ok ist.
1.
Nested-View-Plugin-Homepage für Jenkins
Normalerweise empfiehlt es sich, Jenkins als automatisch beim Booten
startenden Dienst einzurichten.
Dann läuft Jenkins bereits, ohne dass sich ein Benutzer anmelden muss.
Unter Windows erfolgt dies folgendermaßen:
1.
2.
3.
Bei neueren Jenkins-Versionen können Sie folgendermaßen verfahren:
Starten Sie Jenkins (wie oben beschrieben) und klicken Sie unter
"Jenkins verwalten" auf den Menüpunkt "Als Windows-Dienst
installieren".
4.
5.
6.
Falls dieser Menüpunkt "Als Windows-Dienst installieren" bei Ihrer
Jenkins/Hudson-Version fehlt (z.B. in Hudson 1.398),
7.
verfahren Sie folgendermaßen:
8.
9.
1. Entzippen Sie in einem temporären Verzeichnis Ihre hudson...war-Datei (z.B. hudson-1.398.war).
10.
1. Dann entzippen Sie die darin enthaltene WEB-INF\lib\hudson-core1.398.jar.
11.
1. Anschließend kopieren Sie aus dem darin enthaltenen windowsservice-Verzeichnis die
2.
hudson.exe und hudson.xml ins HUDSON_HOME-Verzeichnis.
12.
13.
14.
15.
Öffnen Sie die jenkins.xml im Jenkins-Home-Verzeichnis (z.B.
C:\Tools\Jenkins).
16.
Suchen Sie nach dem Eintrag zur jenkins-...war-Datei (z.B.
jenkins.war) und
17.
korrigieren Sie diesen Eintrag entsprechend dem Namen Ihrer
jenkins-...war-Datei (also z.B. zu jenkins.war).
18.
Suchen Sie weiter nach "--httpPort" und tragen Sie dort die
gewünschte Portnummer ein (z.B. 4424).
19.
20.
21.
22.
Suchen Sie in der Windows-Diensteverwaltung nach dem jenkins-Dienst
über:
"Start" | "Systemsteuerung" | ["System und Sicherheit"] |
"Verwaltung" | "Dienste".
23.
24.
25.
26.
27.
28.
Falls es noch keinen jenkins-Dienst gibt, öffnen Sie ein
Kommandozeilenfenster mit Administratorrechten über:
Start | Alle Programme | Zubehör | "Eingabeaufforderung" mit
rechter Maustaste und "Als Administrator ausführen".
Darin führen Sie aus:
cd C:\Tools\Jenkins
jenkins.exe install
29.
30.
31.
Starten Sie Jenkins entweder erstmalig oder neu über:
"Start" | "Systemsteuerung" | ["System und Sicherheit"] |
"Verwaltung" | "Dienste" | Doppelklick auf "jenkins" |
"Beenden"/"Starten").
32.
33.
34.
35.
Falls Jenkins-Jobs bestimmte Benutzerrechte benötigen (z.B. für den
VCS/SCM-Account), wählen Sie:
"Start" | "Systemsteuerung" | ["System und Sicherheit"] |
"Verwaltung" | "Dienste" | Doppelklick auf "jenkins" | Reiter
"Anmelden" | "Dieses Konto".
36.
37.
38.
Falls Sie beim Starten des Dienstes diese Fehlermeldung erhalten:
39.
Dienst "jenkins" wurde auf "Lokaler Computer" gestartet und dann
angehalten. Einige Dienste werden automatisch angehalten, wenn sie
nicht von anderen Diensten oder Programmen verwendet werden.
40.
und die Windows-Ereignisanzeige (unter Start | Systemsteuerung |
[System und Sicherheit] | Verwaltung | Computerverwaltung | System |
Ereignisanzeige) zeigt an:
41.
Der Dienst kann nicht gestartet werden.
System.ComponentModel.Win32Exception: Das System kann die angegebene
Datei nicht finden.
42.
Dann ersetzen Sie in der jenkins.xml die Zeile:
43.
<executable>java</executable>
durch:
<executable>C:\Program Files\Java\jdk1.7\bin\java</executable>
44.
45.
Falls Sie eine Fehlermeldung erhalten ähnlich zu einer der folgenden:
46.
The system cannot find the file specified.
47.
FATAL: Befehlsausführung fehlgeschlagen.
48.
CreateProcess error=2, Das System kann die angegebene Datei nicht
finden.
49.
ERROR: JAVA_HOME not found in your environment.
50.
Dann ersetzen Sie in der config.xml die Zeile:
51.
<jdks/>
durch:
<jdks>
<jdk>
<name>jdk1.7</name>
<home>C:\Program Files\Java\jdk1.7</home>
<properties/>
</jdk>
</jdks>
52.
53.
Falls Jenkins beim nächsten Windows-Start nicht automatisch startet,
obwohl der Dienste-Starttyp auf "Automatisch" steht,
54.
wählen Sie:
55.
"Start" | "Systemsteuerung" | ["System und Sicherheit"] |
"Verwaltung" | "Dienste" | Doppelklick auf "jenkins"
56.
und dann entweder:
57.
"Starttyp: Automatisch (Verzögerter Start)"
58.
oder:
59.
Reiter "Wiederherstellung" | "Bei Fehlern: Dienst neu starten" und
eine geeignete Wartezeit einstellen.
60.
61.
62.
Falls Jenkins ohne sichtbare Fehlermeldung und ohne Meldung in den
Jenkins-Logdateien nicht automatisch startet,
63.
suchen Sie in der Windows-Ereignisanzeige unter der "Quelle"
"Service Control Manager" nach Meldungen zu Jenkins,
64.
etwa nach folgenden Fehlermeldungen:
65.
Das Zeitlimit (30000 ms) wurde beim Verbindungsversuch mit dem
Dienst jenkins erreicht.
66.
Der Dienst "jenkins" wurde aufgrund folgenden Fehlers nicht
gestartet:
67.
Der Dienst antwortete nicht rechtzeitig auf die Start- oder
Steuerungsanforderung.
68.
69.
70.
71.
Modifizieren Sie entsprechend der Meldung die Startparameter.
Falls Sie bei korrekt installiertem Jenkins-Dienst nur den JenkinsDienst stoppen oder starten wollen, können Sie das net-Kommando
verwenden:
72.
net stop jenkins
net start jenkins
73.
74.
Falls Sie den Jenkins-Dienst deinstallieren wollen: Dies können Sie
wahlweise über
75.
jenkins.exe uninstall
oder
sc delete jenkins
Weitere Themen: andere TechDocs
| Maven-Multimodulprojekte
| maven.apache.org
© 2015 Torsten Horn, Aachen
Herunterladen