Maven Definitive Guide De

550
Thomas Locher (Sonatype, Inc.), Tim O'Brien (Sonatype, Inc.), John Casey (Sonatype, Inc.), Brian Fox (Sonatype, Inc.), Bruce Snyder (Sonatype, Inc.), Jason Van Zyl (Sonatype, Inc.), Eric Redmond () Copyright © 2006-2008

description

 

Transcript of Maven Definitive Guide De

Page 1: Maven Definitive Guide De

Thomas Locher (Sonatype, Inc.), Tim O'Brien (Sonatype, Inc.), John Casey (Sonatype, Inc.),Brian Fox (Sonatype, Inc.), Bruce Snyder (Sonatype, Inc.), Jason Van Zyl (Sonatype, Inc.),

Eric Redmond ()

Copyright © 2006-2008

Page 2: Maven Definitive Guide De

Copyright .......................................................................................................xii1. Creative Commons BY-ND-NC .........................................................xii

Vorwort zur deutschen Ausgabe: 0.5 ............................................................ xivVorwort .......................................................................................................... xv

1. Anleitung zu diesem Buch .................................................................. xv2. Ihr Feedback ....................................................................................... xvi3. Typographische Konventionen .........................................................xvii4. Konventionen in Bezug auf Maven ..................................................xvii5. Danksagung ......................................................................................xviii

1. Einführung von Apache Maven ................................................................... 11.1. Maven ... Was ist das? ........................................................................ 11.2. Konvention über Konfiguration ......................................................... 21.3. Die gemeinsame Schnittstelle ............................................................ 31.4. Universelle Wiederverwendung durch Maven-Plugins ..................... 51.5. Konzeptionelles Modell eines "Projekts" .......................................... 61.6. Ist Maven eine Alternative zu XYZ? ................................................. 81.7. Ein Vergleich von Maven und Ant .................................................... 91.8. Zusammenfassung ............................................................................ 14

2. Installieren und Ausführen von Maven ...................................................... 152.1. Überprüfen der Java-Installation ...................................................... 152.2. Herunterladen von Maven ................................................................ 162.3. Installation von Maven ..................................................................... 16

2.3.1. Maven Installation unter Mac OSX ....................................... 172.3.2. Maven Installation unter Microsoft Windows ....................... 182.3.3. Maven Installation unter Linux .............................................. 192.3.4. Maven Installation unter FreeBSD oder OpenBSD ............... 19

2.4. Testen einer Maven Installation ....................................................... 192.5. Spezielle Installationshinweise ........................................................ 20

2.5.1. Benutzerdefinierte Konfiguration und-Repository ................ 212.5.2. Aktualisieren einer Maven-Installation .................................. 222.5.3. Upgrade einer Maven-Installation von Maven 1.x auf Maven2.x ..................................................................................................... 23

2.6. Maven De-Installieren ...................................................................... 24

ii

Page 3: Maven Definitive Guide De

2.7. Hilfe bekommen beim Arbeiten mit Maven .................................... 242.8. Das Maven Hilfe Plugin ................................................................... 26

2.8.1. Beschreibung eines Maven Plugin ......................................... 272.9. Die Apache Software Lizenz ........................................................... 29

I. Maven by Example ..................................................................................... 313. Ein einfaches Maven Projekt .............................................................. 33

3.1. Einleitung .................................................................................. 333.1.1. Das Herunterladen der Beispiele dieses Kapitels ............ 33

3.2. Erstellen eines einfachen Projekts ............................................. 343.3. Der Aufbau eines einfachen Projekts ........................................ 363.4. Einfaches Projekt Objekt Modell (POM) .................................. 373.5. Kern Konzepte .......................................................................... 39

3.5.1. Maven Plugins und Ziele ................................................. 393.5.2. Maven Lifecycle .............................................................. 423.5.3. Maven Koordinaten ......................................................... 463.5.4. Maven Repositories ......................................................... 493.5.5. Maven Abhängigkeits-Management (DependencyManagement) ............................................................................. 523.5.6. Site-Generierung und Reporting ..................................... 55

3.6. Zusammenfassung ..................................................................... 564. Anpassen eines Maven Projektes ........................................................ 57

4.1. Einleitung .................................................................................. 574.1.1. Herunterladen der Beispiele dieses Kapitels ................... 57

4.2. Eine kurze Einführung in das "Simple Weather" Projekt ......... 574.2.1. Yahoo! Wetter Dienst RSS ............................................. 58

4.3. Erstellen des "Simple Weather" Projektes ................................ 594.4. Anpassen der Projektinformationen .......................................... 604.5. Einfügen neuer Abhängigkeiten ................................................ 624.6. Quellcode von "Simple Weather" ............................................. 644.7. Resourcen Hinzufügen .............................................................. 714.8. Ausführen des "Simple Weather" Programms .......................... 72

4.8.1. Das Exec Maven Plugin .................................................. 744.8.2. Erkundung der Projekt Abhängigkeiten .......................... 74

4.9. Erstellen von Unit-Tests ............................................................ 77

Maven: The Definitive Guide

iii

Page 4: Maven Definitive Guide De

4.10. Hinzufügen von Gebietsbezogenen Unit Tests ....................... 804.11. Hinzufügen einer Unit-Test Ressource ................................... 814.12. Ausführen von Unit-Tests ....................................................... 83

4.12.1. Ignorieren fehlgeschlagener Unit Tests ......................... 844.12.2. Überspringen von Unit-Tests ........................................ 86

4.13. Builden einer paketierten, Befehlszeilen orientiertenAnwendung ...................................................................................... 87

4.13.1. Anbinden des Assembly Goals zur Packetierungs Phase 895. Eine einfache Web-Anwendung ......................................................... 91

5.1. Einleitung .................................................................................. 915.1.1. Herunterladen der Beispiele dieses Kapitels ................... 91

5.2. Eine kurze Einführung in die "Simple Web" Anwendung ........ 915.3. Erstellen des "Simple Web" Projekts ........................................ 925.4. Konfigurieren des Jetty-Plugins ................................................ 935.5. Das Hinzufügen eines einfachen Servlets ................................. 965.6. Das Hinzufügen von J2EE-Abhängigkeiten ............................. 985.7. Zusammenfassung ................................................................... 100

6. Ein multi-modulares Projekt ............................................................. 1016.1. Einleitung ................................................................................ 101

6.1.1. Herunterladen der Beispiele dieses Kapitels ................. 1016.2. Das "Simple Parent" Projekt (parent=Elternteil) .................... 1026.3. Das "Simple Weather" Modul ................................................. 1036.4. Das "Simple-Web" Anwendungs-Modul ................................ 1066.5. Erstellung des Multi-Projekt-Moduls ...................................... 1096.6. Starten der Web-Anwendung .................................................. 110

7. Multi-module Enterprise Project ....................................................... 1127.1. Einleitung ................................................................................ 112

7.1.1. Herunterladen der Beispiele dieses Kapitels ................. 1127.1.2. Multi-Modul Enterprise Projekt .................................... 1137.1.3. Technologie des Beispiels ............................................. 116

7.2. Das "Simple Parent" Projekt - Top Level ............................... 1177.3. Das Simple Model" Modul - Das Objektmodell ..................... 1197.4. Das "Simple Weather" Modul - Die Dienste .......................... 1257.5. Das "Simple Persist" Modul - Die Datenabstraktion .............. 129

Maven: The Definitive Guide

iv

Page 5: Maven Definitive Guide De

7.6. Das "Simple Web" Modul - Das Web-Interface ..................... 1397.7. Aufrufen der Web-Anwendung ............................................... 1537.8. Das "Simple Command" Modul - Das Kommandozeilen Modul......................................................................................................... 1547.9. Aufrufen der Kommandozeilen-Anwendung .......................... 1627.10. Fazit ....................................................................................... 164

7.10.1. Programmierung gegen Interface-Projekte ................. 1668. Optimirung und Überarbeitung der POMs ........................................ 168

8.1. Einführung ............................................................................... 1688.2. POM Bereinigung ................................................................... 1698.3. Optimirung der Abhängigkeiten ............................................. 1708.4. Optimirung der Plugins ........................................................... 1768.5. Optimierung unter Zuhilfenahmen des Maven Dependency Plugin......................................................................................................... 1788.6. Abschliessende POMs ............................................................. 1828.7. Fazit ......................................................................................... 190

II. Maven Reference ..................................................................................... 1929. The Project Object Model ................................................................. 193

9.1. Introduction ............................................................................. 1939.2. The POM ................................................................................. 193

9.2.1. The Super POM ............................................................. 1969.2.2. The Simplest POM ........................................................ 2009.2.3. The Effective POM ....................................................... 2019.2.4. Real POMs .................................................................... 201

9.3. POM Syntax ............................................................................ 2029.3.1. Project Versions ............................................................ 2029.3.2. Property References ...................................................... 205

9.4. Project Dependencies .............................................................. 2079.4.1. Dependency Scope ........................................................ 2089.4.2. Optional Dependencies ................................................. 2109.4.3. Dependency Version Ranges ........................................ 2119.4.4. Transitive Dependencies ............................................... 2139.4.5. Conflict Resolution ....................................................... 2159.4.6. Dependency Management ............................................. 217

Maven: The Definitive Guide

v

Page 6: Maven Definitive Guide De

9.5. Project Relationships ............................................................... 2199.5.1. More on Coordinates ..................................................... 2209.5.2. Multi-module Projects ................................................... 2219.5.3. Project Inheritance ......................................................... 223

9.6. POM Best Practices ................................................................. 2279.6.1. Grouping Dependencies ................................................ 2279.6.2. Multi-module vs. Inheritance ........................................ 229

10. Der Build Lebenszyklus .................................................................. 23710.1. Einführung ............................................................................. 237

10.1.1. Lebenszyklus: clean .................................................... 23710.1.2. Standard Lebenszyklus: default .................................. 24110.1.3. Lebenszyklus: site ....................................................... 243

10.2. Package-spezifische Lebenszyklen ....................................... 24410.2.1. jar ................................................................................. 24510.2.2. pom .............................................................................. 24610.2.3. plugin ........................................................................... 24610.2.4. ejb ................................................................................ 24710.2.5. war ............................................................................... 24810.2.6. ear ................................................................................ 24810.2.7. Andere Packetierungs Typen ...................................... 249

10.3. Gebräuchliche Lebenszyklus Goals ...................................... 25110.3.1. Ressourcen Verarbeiten ............................................... 25110.3.2. compile ........................................................................ 25510.3.3. Verarbeiten von Test Ressourcen ................................ 25710.3.4. Kompilieren der Test Klassen (testCompile) .............. 25810.3.5. test ............................................................................... 25810.3.6. install ........................................................................... 26010.3.7. deploy .......................................................................... 260

11. Build Profiles .................................................................................. 26211.1. What Are They For? .............................................................. 262

11.1.1. What is Build Portability ............................................. 26211.1.2. Selecting an Appropriate Level of Portability ............. 264

11.2. Portability through Maven Profiles ....................................... 26511.2.1. Overriding a Project Object Model ............................. 268

Maven: The Definitive Guide

vi

Page 7: Maven Definitive Guide De

11.3. Profile Activation .................................................................. 26911.3.1. Activation Configuration ............................................. 27111.3.2. Activation by the Absence of a Property .................... 272

11.4. Listing Active Profiles .......................................................... 27311.5. Tips and Tricks ...................................................................... 274

11.5.1. Common Environments ............................................... 27411.5.2. Protecting Secrets ........................................................ 27611.5.3. Platform Classifiers ..................................................... 278

11.6. Summary ............................................................................... 28012. Maven Assemblies .......................................................................... 282

12.1. Introduction ........................................................................... 28212.2. Assembly Basics ................................................................... 283

12.2.1. Predefined Assembly Descriptors ............................... 28412.2.2. Building an Assembly ................................................. 28512.2.3. Assemblies as Dependencies ....................................... 28812.2.4. Assembling Assemblies via Assembly Dependencies 289

12.3. Overview of the Assembly Descriptor .................................. 29312.4. The Assembly Descriptor ...................................................... 296

12.4.1. Property References in Assembly Descriptors ............ 29612.4.2. Required Assembly Information ................................. 296

12.5. Controlling the Contents of an Assembly ............................. 29812.5.1. Files Section .............................................................. 29812.5.2. FileSets Section ......................................................... 29912.5.3. Default Exclusion Patterns for fileSets .................... 30212.5.4. dependencySets Section ............................................. 30312.5.5. moduleSets Sections ................................................... 31712.5.6. Repositories Section .................................................... 32512.5.7. Managing the Assembly’s Root Directory .................. 32612.5.8. componentDescriptors andcontainerDescriptorHandlers ............................................. 327

12.6. Best Practices ........................................................................ 32812.6.1. Standard, Reusable Assembly Descriptors .................. 32812.6.2. Distribution (Aggregating) Assemblies ...................... 332

12.7. Summary ............................................................................... 337

Maven: The Definitive Guide

vii

Page 8: Maven Definitive Guide De

13. Properties and Ressource Filterung ................................................. 33813.1. Einleitung .............................................................................. 33813.2. Maven Properties .................................................................. 338

13.2.1. Maven Projekt Einstellungen ...................................... 33913.2.2. Properties der Maven Einstellungen (settings.xml) .... 34213.2.3. Properties der Umgebungsvariablen ........................... 34213.2.4. Java System Properties ................................................ 34313.2.5. Benuzerdefinierte Properties ....................................... 345

13.3. Ressource Filterung ............................................................... 34614. Maven in Eclipse: m2eclipse .......................................................... 351

14.1. Einführung ............................................................................. 35114.2. m2eclipse ............................................................................... 35114.3. Installation des m2eclipse Plugins ........................................ 352

14.3.1. Installieren der Voraussetzungen ................................ 35314.3.2. Installation von m2eclipse ........................................... 355

14.4. Aufschalten der Maven Konsole ........................................... 35614.5. Erstellen eines Maven Projekts ............................................. 357

14.5.1. Auschecken eines Maven Projektes von einem SCMRepository ............................................................................... 35814.5.2. Erstellen eines Maven Projekts auf der Basis eines MavenArchetyps ................................................................................ 36014.5.3. Erstellen eines Maven Moduls .................................... 364

14.6. Erstellen einer Maven POM Datei ........................................ 36714.7. Importieren von Maven Projekten ........................................ 371

14.7.1. Importiren eines Maven Projektes ............................... 37314.7.2. Materialisieren eines Maven Projektes ....................... 375

14.8. Starten von Maven Builds ..................................................... 37914.9. Mit Maven Projekten arbeiten ............................................... 382

14.9.1. Zufügen und Updaten von Abhängigkeiten und Plugins 38414.9.2. Erstellen eines Maven Modules .................................. 38714.9.3. Herunterladen der Quelldatei(en) ................................ 38714.9.4. Öffnen von Projektseiten ............................................. 38714.9.5. Auflösen von Abhängigkeiten ..................................... 388

14.10. Arbeiten mit den Maven Repositorien ................................ 388

Maven: The Definitive Guide

viii

Page 9: Maven Definitive Guide De

14.10.1. Suchen von Maven Artefakten sowie Java Klassen .. 38914.10.2. Indizierung von Maven Repositorien ........................ 394

14.11. Der neue graphische POM Editor ....................................... 39814.12. Projektabhängigkeiten mit m2eclipse analysieren .............. 40414.13. Maven Einstellungen ........................................................... 40914.14. Zusammenfassung ............................................................... 417

15. Site Generation ................................................................................ 41915.1. Introduction ........................................................................... 41915.2. Building a Project Site with Maven ...................................... 42015.3. Customizing the Site Descriptor ........................................... 422

15.3.1. Customizing the Header Graphics ............................... 42315.3.2. Customizing the Navigation Menu ............................. 424

15.4. Site Directory Structure ......................................................... 42615.5. Writing Project Documentation ............................................ 427

15.5.1. APT Example .............................................................. 42715.5.2. FML Example ............................................................. 428

15.6. Deploying Your Project Website .......................................... 42915.6.1. Configuring Server Authentication ............................. 43015.6.2. Configuring File and Directory Modes ....................... 431

15.7. Customizing Site Appearance ............................................... 43215.7.1. Customizing the Site CSS ........................................... 43215.7.2. Create a Custom Site Template ................................... 43315.7.3. Reusable Website Skins .............................................. 43815.7.4. Creating a Custom Theme CSS ................................... 44015.7.5. Customizing Site Templates in a Skin ........................ 441

15.8. Tips and Tricks ...................................................................... 44315.8.1. Inject XHTML into HEAD ......................................... 44315.8.2. Add Links under Your Site Logo ................................ 44315.8.3. Add Breadcrumbs to Your Site ................................... 44415.8.4. Add the Project Version .............................................. 44515.8.5. Modify the Publication Date Format and Location ..... 44615.8.6. Using Doxia Macros .................................................... 447

16. Repository Management with Nexus .............................................. 44917. Writing Plugins ............................................................................... 451

Maven: The Definitive Guide

ix

Page 10: Maven Definitive Guide De

17.1. Introduction ........................................................................... 45117.2. Programming Maven ............................................................. 451

17.2.1. What is Inversion of Control? ..................................... 45217.2.2. Introduction to Plexus ................................................. 45317.2.3. Why Plexus? ................................................................ 45417.2.4. What is a Plugin? ......................................................... 455

17.3. Plugin Descriptor .................................................................. 45617.3.1. Top-level Plugin Descriptor Elements ........................ 45817.3.2. Mojo Configuration ..................................................... 45917.3.3. Plugin Dependencies ................................................... 463

17.4. Writing a Custom Plugin ....................................................... 46317.4.1. Creating a Plugin Project ............................................. 46317.4.2. A Simple Java Mojo .................................................... 46417.4.3. Configuring a Plugin Prefix ........................................ 46617.4.4. Logging from a Plugin ................................................ 47017.4.5. Mojo Class Annotations .............................................. 47117.4.6. When a Mojo Fails ...................................................... 473

17.5. Mojo Parameters ................................................................... 47417.5.1. Supplying Values for Mojo Parameters ...................... 47417.5.2. Multi-valued Mojo Parameters .................................... 47717.5.3. Depending on Plexus Components ............................. 47917.5.4. Mojo Parameter Annotations ...................................... 479

17.6. Plugins and the Maven Lifecycle .......................................... 48117.6.1. Executing a Parallel Lifecycle ..................................... 48117.6.2. Creating a Custom Lifecycle ....................................... 48217.6.3. Overriding the Default Lifecycle ................................ 484

18. Writing Plugins in Alternative Languages ...................................... 48718.1. Writing Plugins in Ant .......................................................... 48718.2. Creating an Ant Plugin .......................................................... 48718.3. Writing Plugins in JRuby ...................................................... 490

18.3.1. Creating a JRuby Plugin .............................................. 49118.3.2. Ruby Mojo Implementations ....................................... 49318.3.3. Logging from a Ruby Mojo ........................................ 49618.3.4. Raising a MojoError .................................................... 497

Maven: The Definitive Guide

x

Page 11: Maven Definitive Guide De

18.3.5. Referencing Plexus Components from JRuby ............ 49718.4. Writing Plugins in Groovy .................................................... 498

18.4.1. Creating a Groovy Plugin ............................................ 49919. Using Maven Archetypes ................................................................ 501

19.1. Introduction to Maven Archetypes ........................................ 50119.2. Using Archetypes .................................................................. 502

19.2.1. Using an Archetype from the Command Line ............ 50219.2.2. Using the Interactive generate Goal ............................ 50319.2.3. Using an Archetype from m2eclipse ........................... 506

19.3. Available Archetypes ............................................................ 50619.3.1. Common Maven Archetypes ....................................... 50619.3.2. Notable Third-Party Archetypes ................................. 508

19.4. Publishing Archetypes .......................................................... 511A. Appendix: Detailinformationen zur settings.xml-Datei .......................... 514

A.1. Übersicht ....................................................................................... 514A.2. Die Details der settings.xml Datei ................................................ 515

A.2.1. Einfache Wertangaben ........................................................ 515A.2.2. Servers ................................................................................. 516A.2.3. Spiegelrepositorien .............................................................. 517A.2.4. Proxies ................................................................................. 518A.2.5. Profiles ................................................................................ 520A.2.6. Activation ............................................................................ 520A.2.7. Properties ............................................................................. 522A.2.8. Repositories ......................................................................... 523A.2.9. Plugin Repositories ............................................................. 525A.2.10. Aktive Profile .................................................................... 526

B. Appendix: Alternativen zu den Sun Spezifikationen .............................. 528

Maven: The Definitive Guide

xi

Page 12: Maven Definitive Guide De

CopyrightCopyright 2008 Sonatype, Inc.

Online version published by Sonatype, Inc., 654 High Street, Suite 220, Palo Alto,CA, 94301.

Print version published by O'Reilly Media, Inc., 1005 Gravenstein Highway North,Sebastopol, CA 95472.

Nutshell Handbook, the Nutshell Handbook logo, and the O'Reilly logo areregistered trademarks of O'Reilly Media, Inc. The Developer's Notebook seriesdesignations, the look of a laboratory notebook, and related trade dress aretrademarks of O'Reilly Media, Inc.

Java(TM) and all Java-based trademarks and logos are trademarks or registeredtrademarks of Sun Microsystems, Inc., in the United States and other countries.Many of the designations used by manufacturers and sellers to distinguish theirproducts are claimed as trademarks. Where those designations appear in this book,and Sonatype, Inc. was aware of a trademark claim, the designations have beenprinted in caps or initial caps.

While every precaution has been taken in the preparation of this book, thepublisher and authors assume no responsibility for errors or omissions, or fordamages resulting from the use of the information contained herein.

1. Creative Commons BY-ND-NCThis work is licensed under a Creative Commons Attribution-Noncommercial-NoDerivative Works 3.0 United States license. For more information about thislicense, see http://creativecommons.org/licenses/by-nc-nd/3.0/us/. You are free toshare, copy, distribute, display, and perform the work under the followingconditions:

• You must attribute the work to Sonatype, Inc. with a link tohttp://www.sonatype.com.

xii

Page 13: Maven Definitive Guide De

• You may not use this work for commercial purposes.

• You may not alter, transform, or build upon this work.If you redistribute this work on a web page, you must include the following linkwith the URL in the about attribute listed on a single line (remove the backslashesand join all URL parameters):

<div xmlns:cc="http://creativecommons.org/ns#"about="http://creativecommons.org/license/results-one?q_1=2&q_1=1\

&field_commercial=n&field_derivatives=n&field_jurisdiction=us\&field_format=StillImage&field_worktitle=Maven%3A+\Guide\&field_attribute_to_name=Sonatype%2C+Inc.\&field_attribute_to_url=http%3A%2F%2Fwww.sonatype.com\&field_sourceurl=http%3A%2F%2Fwww.sonatype.com%2Fbook\&lang=en_US&language=en_US&n_questions=3">

<a rel="cc:attributionURL" property="cc:attributionName"href="http://www.sonatype.com">Sonatype, Inc.</a> /

<a rel="license"href="http://creativecommons.org/licenses/by-nc-nd/3.0/us/">

CC BY-NC-ND 3.0</a></div>

When downloaded or distributed in a jurisdiction other than the United States ofAmerica, this work shall be covered by the appropriate ported version of CreativeCommons Attribution-Noncommercial-No Derivative Works 3.0 license for thespecific jurisdiction. If the Creative Commons Attribution-Noncommercial-NoDerivative Works version 3.0 license is not available for a specific jurisdiction, thiswork shall be covered under the Creative CommonsAttribution-Noncommercial-No Derivate Works version 2.5 license for thejurisdiction in which the work was downloaded or distributed. A comprehensivelist of jurisdictions for which a Creative Commons license is available can befound on the Creative Commons International web site athttp://creativecommons.org/international.

If no ported version of the Creative Commons license exists for a particularjurisdiction, this work shall be covered by the generic, unported CreativeCommons Attribution-Noncommercial-No Derivative Works version 3.0 licenseavailable from http://creativecommons.org/licenses/by-nc-nd/3.0/.

Copyright

xiii

Page 14: Maven Definitive Guide De

Vorwort zur deutschen Ausgabe: 0.5Vor Ihnen liegt die deutsche Übersetzung der "Definitiv Guide to Apache Maven".Ein Werk das, wie das englische Original, zum Ziel hat Brücken zu schlagen.Brücken nicht nur zwischen Technikern und Techniken, sondern auch Brückenzwischen unseren verschiedenen Kulturen.

In meiner täglichen Erfahrung gibt es eine grosse Zahl Arbeitsgruppen und Teamswelche mehr als eine, oft sogar so viele Nationalitäten wie Mitglieder umfassen.Das Kommunikationsmedium ist meist der kleinste gemeinsame Nenner:(Sowie-)Englisch. Wie oft habe ich nach tage- und nächtelangem Suchenfeststellen müssen, dass der Fehler eigentlich ganz offensichtlich war, wennMan(n) nur die (englische) Anleitung verstanden hätte!

Maven ist ein mächtiges Tool, häufiger missverstanden und nur rudimentäreingesetzt - insbesondere weil die Zusammenhänge unklar, die Dokumentation inEnglisch schwierig und lückenhaft ist. Da ich vom Wert dieses Werkzeugsüberzeugt bin habe ich alles gegeben dieses Werkzeug auch dem deutschenSprachraum "native" zugänglich zu machen.

Ich erhoffe mir hiervon auch, Entwickler zu ermutigen sich in der grossenbestehenden Maven Community einzubringen, Themen aufzugreifen und weiter zuführen. Diesen Entwicklern möchte ich Sicherheit bieten indem ich Ihnen eineReferenz zur Seite stelle, die sie verstehen.

Mit Blick auf unsere multinationalen Gruppen habe ich versucht das Werk so naheam englischen Original zu belassen als möglich. Sie werden also aufAbschnittebene vergleichen können. Sollten Sie unsicher sein was Ihr Kollegetatsächlich meint, so zücken Sie Ihr deutsches Exemplar, sehen nach welchenAbschnitt im englischen Original Ihr Kollege referenziert - et voilà - lesen Sie inIhrer Muttersprache was gemeint ist.

Thomas Locher

Zürich, im Januar 2008

PS: Habe ich Ihnen bereits empfohlen die englische Originalausgabe zu kaufen?

xiv

Page 15: Maven Definitive Guide De

VorwortMaven is ein Build Werkzeug, ein Projekt Management Werkzeug ein abstrakterContainer um Build Aufträge abzuarbeiten. Es stellt ein Werkzeug dar, welchesunumgänglich ist für alle Projekte welche über die einfachsten Anforderungenhinauswachsen und sich plötzlich in der Lage finden konsistent eine grosse Zahlvoneinander abhängiger Bibliotheken zu Builden, welche sich auf dutzende odergar hunderte Komponenten (auch) von Drittparteien abstützen. Es ist einWerkzeug, das dazu beigetragen hat einen Grossteil des Verwaltungsaufwandes fürKomponenten aus dem Alltag von Millionen von Fachspezialisten zu eliminieren.Es hat vielen Organisationen geholfen sich weiterzuentwickeln, hinweg über diePhase des täglichen Kampf mit dem Build Management, hin zu einer neuen Phasein welcher der Aufwand einen Build zu erhalten und zu pflegen nicht mehr eineGrenzgrösse des Software Designs darstellt.

Dieses Buch ist ein erster Versuch eines umfassenden Werkes bezüglich Maven.Es baut auf die gesammelten Erfahrungen und der Arbeit aller Autorenvorgängiger Titel bezüglich Maven auf und Sie sollten dieses Werk nicht alsfertiges Werkstück ansehen sondern vielmehr als der erster Wurf einer langenReihe von Updates welche dieser Ausgabe folgen werden. Auch wenn es Mavenbereits einige Jahre gibt, so empfinden die Autoren dieses Werkes dennoch, dassMaven erst gerade begonnen hat die kühnen Versprechen einzulösen welche aufden Weg gegeben wurden. Die Autoren -wie auch die Firma- welche hinter diesemBuch stehen, Sonatype glauben fest daran, dass dieses Werk dazu beitragen wird,eine neue Welle der Innovation und Entwicklung in Umfeld von Maven unddessen Ökosystem loszutreten.

1. Anleitung zu diesem BuchNehmen Sie dieses Buch in die Hand und lesen Sie einige Seiten des Textes. AmEnde der Seite werden Sie, sollten Sie die HTML Ausgabe lesen, entweder denLink zur nächsten Seite folgen wollen, oder im Fall der gedruckten Ausgabewerden Sie die Ecke heben und weiterblättern. Sollten Sie vor einem Computer

xv

Page 16: Maven Definitive Guide De

sitzen, so können Sie sicherlich das eine oder andere Beispiel durchspielen um demText zu folgen. Was Sie nicht tun sollten, auch nicht in Wut, ist das Buch in dieRichtung eines Mitmenschen zu werfen - es ist einfach zu schwer!

Dieses Buch gliedert sich in drei Abschnitte: eine Einführung, einen Beispielteilund eine Referenz. Die Einführung besteht aus zwei Kapiteln: Einführung undInstallation. Der Beispielteil führt Maven ein, indem einige echteAnwendungsbeispiele eingeführt werden und Ihnen anhand der Struktur derBeispiele weitere Motivation und Erklärung gegeben werden. Sollte Maven für Sieneu sein, so fangen Sie an, den Beispielteil zu lesen. Die Referenz ist weniger alsEinführung gedacht, sondern mehr als Nachschlagewerk. Jedes Kapitel desReferenzteils ziehlt genau auf ein Thema ab und geht hierbei auf dietiefstmöglichen Details ein. So wird das Kapitel über die Erstellung von Pluginsanhand von einigen Beispielen und einer Anzahl Listen und Tabellen abgehandelt.

Beide Abschnitte bieten Erklärungen und doch werden grundverschiedene Wegeeingeschlagen. Während der beispielhafte Teil sich auf den Zusammenhanginnerhalb des Maven Projekt stützt, konzentriert sich der Referenzteil jeweils aufein einziges Thema. Sie können natürlich auch im Buch herumspringen; derbeispielhafte Teil ist in keiner Weise Voraussetzung für den Referenzteil, aber Siewerden den Referenzteil besser verstehen, sollten Sie bereits den beispielhaftenTeil gelesen haben. Maven wird am Besten anhand von Beispielen gelernt, sobaldSie die Beispiele durchgeabeitet haben werden Sie eine gute Referenzquellebrauchen, um sich Maven Ihren Bedürfnissen anzupassen.

2. Ihr FeedbackWir haben dieses Buch nicht geschrieben, um unserem Herausgeber ein WordDokument zu senden, eine Veröffentlichung zu feiern und einander zuBeglückwünschen was für eine gute Arbeit wir geleistet haben. Dieses Buch istnicht "fertig", ja dieses Buch wird wohl nie "fertig" werden. Das behandelte Themaändert sich ständig, weitet sich aus; und so verstehen wir dieses Buch als einständig weitergehender Dialog mit der Maven Community. Dieses Buchherauszugeben bedeutet, dass die wahre Arbeit erst gerade begonnen hat, und Sie,der Leser, spielen eine zentrale Rolle dieses Buch zu erweitern, verbessern und

Vorwort

xvi

Page 17: Maven Definitive Guide De

aktuell zu halten. Sollten Sie etwas bemerken das fehlerhaft ist: einRechtschreibfehler, schlechter Code, eine offenkundige Lüge, dann teilen Sie unsdies mit! Schreiben Sie uns eine Email an: [email protected].

Die weitergehende Rolle dieses Buches ist abhängig von Ihrem Feedback. Wir sindinteressiert daran zu erfahren, was funktioniert und was nicht. Wir würden gerneerfahren, sollte dieses Buch Informationen enthalten welche Sie nicht verstehen.Ganz besonders wollen wir erfahren, sollten Sie den Eindruck haben dises Buchwäre schrecklich. Positive und negative Rückmeldungen sind uns willkommen.Wir nehmen uns das Recht heraus nicht jeder Meinung zuzustimmen, aber injedem Fall bekommen Sie eine gebührende Antwort.

3. Typographische KonventionenDieses Buch verwendet die folgenden typographischen Konventionen:

KursivWird für wichtige Begriffe, Programm- und Dateinamen, URLs, Ordner undVerzeichnispfade und zur Hervorhebung/Einführung von Begriffen verwendet.

Nichtproportonalschrift

Wird für Programmcode verwendet (Java Klassennamen, -methoden,Variablen, Properties, Datentypen, Datenbank Elemente, und andereCodebeispiele welche in den Text eingestreut sind).

Fette NichtproportonalschriftWird verwendet für Befehle welche Sie auf der Kommandozeile eingeben,sowie um neue Codefragmente in fortlaufenden Beispielen hervorzuheben.

Kursive Nichtproportonalschrift

Wird verwendet um Ausgabetext zu kennzeichnen.

4. Konventionen in Bezug auf Maven

Vorwort

xvii

Page 18: Maven Definitive Guide De

Dieses Buch folgt gewissen Konventionen bezüglich Namensgebung undSchriftsatz bezüglich Apache Maven.

Compiler PluginMaven Plugins werden gross geschrieben.

Goal create

Maven Goal Namen werden in nichtproportionaler Schrift wiedergegeben.

"plugin"Maven kreist stark um die Nutzung von Plugins, aber Sie werden keineumfassende Definition von Plugin in einem Wörterbuch finden. Dieses Buchbenutzt den Terminus Plugin, da er einprägsam ist und auch weil er sich in derMaven Community so eingebürgert hat.

Maven Lebenszyklus, Maven Standard Verzeichnis Layout, Maven Plugin,Projekt Objekt ModellDie Kernkonzepte von Maven werden im Text gross geschrieben, insbesonderewenn darauf Bezug genommen wird.

Goal Parameter

Ein Parameter eines Maven Goal wird in nichtproportionalschriftwiedergegeben.

Phase compile

Lebenszyklusphasen werden in nichtproportionalschrift wiedergegeben.

5. DanksagungSonatype möchte sich insbesondere bei allen Mitwirkenden bedanken: die folgendeListe ist in keiner Weise abschliessend oder vollständig und alle haben mitgewirktum die Qualität dieses Werkes zu verbessern. Vielen Dank an MangalaganeshBalasubramanian, Bertrand Florat, Chad Gorshing, Ali Colabawala, BozhidarBatzov sowie Mark Stewart. Besonderen Dank auch an Joel Costigliola für dieHilfe und Korrektur des Kapitels bezüglich Spring Web. Stan Guillory war schon

Vorwort

xviii

Page 19: Maven Definitive Guide De

so etwas wie ein Teilautor, zählt man die grosse Zahl der Korrekturen undVerbesserungsvorschläge welcher er einbrachte. Vielen Dank Stan. SpeziellenDank auch an Richard Coatsby von Bamboo der die Rolle des einstweiligenGrammatik-Beraters aufgenommen hat.

Vielen Dank an alle partizipierenden Teilautoren einschliesslich Eric Redmond.

Vielen Dank auch an die folgenden Mitwirkenden welche Fehler entweder in Formeiner Email oder zum zugehörigen Get Satisfaction Site gemeldet haben: PacoSoberón, Ray Krueger, Steinar Cook, Henning Saul, Anders Hammar,"george_007", "ksangani", Niko Mahle, Arun Kumar, Harold Shinsato, "mimil","-thrawn-", Matt Gumbley. Sollten Sie Ihren Get Satisfaction Benutzernamen indieser Liste aufgeführt sehen und würden diesen gerne durch Ihren Namen ersetztwissen, so senden Sie uns eine kurze Mail an [email protected].

Vorwort

xix

Page 20: Maven Definitive Guide De

Chapter 1. Einführung von Apache MavenEs gibt zwar schon eine Reihe Online Veröffentlichungen zum Thema ApacheMaven, aber was bis heute fehlt, ist eine umfassende, gut geschriebenenEinführung von Maven, welche zugleich als altgediente Referenz herhalten kann.Was wir hier versucht haben, ist eine solche gesamthafte Einführung gepaart miteiner umfassenden Referenz zu erstellen.

1.1. Maven ... Was ist das?Die Antwort auf diese Frage hängt ganz von Ihrer Perspektive ab. Die großeMehrheit der Benutzer wird Maven als "Build"-Werkzeug einordnen: einWerkzeug um aus Quellcode einsatzfähige Artefakte (Programme) zu erzeugen.Build Ingenieure und Projektmanager mögen Maven als etwas umfassenderessehen: ein (technisches) Projektmanagement-Tool. Wo liegt der Unterschied? EinBuild-Werkzeug wie Ant ist ausschließlich auf die Vorverarbeitung,Kompilierung, Prüfung und Verteilung ausgelegt. Ein Projekt-Management-Toolwie Maven liefert eine Obermenge von Funktionen welche in einem Build- undPaketier-Werkzeug zur Anwendung kommen. Neben der Bereitstellung von BuildFunktionalitäten, bietet Maven auch die Möglichkeit Berichte/Auswertungen(Reports) anzufertigen, Websites zu generieren und den Kontakt zwischen denMitgliedern einer Arbeitsgruppe/eines Teams zu ermöglichen.

Hier eine formale Definition von Apache Maven: Maven ist einProjektmanagement-Tool welches ein umfassendes Projektmodell beinhaltet undeine Reihe von Normen bereitstellt. Darüber hinaus verfügt es über einendefinierten Projekt-Lebenszyklus, ein Abhängigkeits-Management-System sowieLogik, welche die Ausführung von Plugin-Goals in definierten Phasen einesLebenszykluses ermöglicht. Wenn Sie Maven einsetzen, beschreiben Sie IhrProjekt in einem genau definierten Projekt Objekt Modell, im weiteren Text POMgenannt. Maven kann dann auf dieses Modell überspannende Regelwerke aus derMenge gemeinsam genutzter (oder auch massgeschneiderter) Plugins anwenden.

Lassen Sie sich nicht von der Tatsache dass Maven ein "Projektmanagement"

1

Page 21: Maven Definitive Guide De

Werkzeug darstellt, erschrecken. Sollten Sie lediglich nach einem Build-ToolAusschau gehalten haben, so wird Maven Ihnen dies ebenfalls bieten. In der Tat,die ersten Kapitel dieses Buches befassen sich mit dem häufigstenAnwendungsfall: Der Verwendung von Maven um Ihr Projekt zu kompilieren,testen und zu verteilen.

1.2. Konvention über KonfigurationKonvention über Konfiguration ist ein einfaches Konzept: Systeme, Bibliothekenund Frameworks sollten von vernünftige Standardwerten ausgehen, ohne unnötigeKonfigurationen zu benötigen - Systeme sollten "einfach funktionieren". GängigeFrameworks wie Ruby on Rails und EJB3 haben damit begonnen, sich an diesenGrundsätzen zu orientieren. Dies in Reaktion auf die Komplexität derKonfiguration von Frameworks wie etwa der anfänglichen EJB 2.1Spezifikationen. Das Konzept 'Konvention über Konfiguration' wird am Beispielder EJB 3 Persistenz schön veranschaulicht: alles, was Sie tun müssen, um einbestimmtes Bean persistent zu speichern ist, dieses mit einer @Entitiy-Annotationzu versehen. Das Framework übernimmt Tabellen-und Spaltennamen basierend aufden Namen der Klasse und den Namen der Attribute. Es besteht die Möglichkeit,mittels sogenannter 'Hooks' - wenn nötig - die gesetzten Standardwerte zuübersteuern. In den meisten Fällen werden Sie feststellen, dass der Einsatz der vomFramework bereitgestellten Namen zu einer schnelleren Projektdurchführung führt.

Maven greift dieses Konzept auf und stellt vernünftige Standard-Verhalten fürProjekte bereit. Ohne weitere Anpassungen, wird davon ausgegangen, dass sichQuellcode im Verzeichnis ${basedir}/src/main/java und Ressourcen sich imVerzeichnis ${basedir}/src/main/resources befinden. Von Tests wird davonausgegangen, dass diese sich im Verzeichnis ${basedir}/src/test befinden, sowieein Projekt standardmäßig ein JAR-Archive bereitstellt. Maven geht davon aus,dass Sie den kompilierten Byte-Code unter ${basedir}/target/classes ablegenwollen und anschließend ein ausführbares JAR-Archive erstellen, welches imVerzeichnis ${basedir}/target abgelegt wird. Zwar mag diese Lösung trivialanmuten, jedoch bedenken Sie bitte die Tatsache, dass die meisten Ant-basiertenProjekte die Lage dieser Verzeichnisse in jedem Teilprojekt festlegen müssen. Die

Einführung von Apache Maven

2

Page 22: Maven Definitive Guide De

Umsetzung von Konvention über Konfiguration in Maven geht noch viel weiter alsnur einfache Verzeichnis Standorte festzulegen, Maven-Core-Plugins kennen eineinheitliches System von Konventionen für die Kompilierung von Quellcode, derPaketierung sowie Verteilung der Artefakten, der Generierung von(Dokumentations-) Web-Seiten, und vielen anderen Prozessen. Die Stärke vonMaven beruht auf der Tatsache, dass Maven mit einer 'vorbestimmten Meinung'ausgelegt ist: Maven hat einen definierten Lebenszyklus sowie eine Reihe vonPlugins welche unter gemeinsamen Annahmen in der Lage sind, Software zuerstellen. Sollten Sie nach den Konventionen arbeiten, erfordert Maven fast keinenAufwand - einfach Ihre Quellen in das richtige Verzeichnis legen - Mavenkümmert sich um den Rest.

Ein Nachteil der Verwendung von Systemen welche dem Grundsatz derKonvention über Konfiguration folgen ist, dass Endbenutzer oftmals die Gefahrsehen, dass sie gezwungen werden eine bestimmte Methode anzuwenden - einembestimmten Ansatz folgen müssen. Zwar ist es sicherlich richtig, dass der Kern vonMaven auf einigen wenigen Grundannahmen basiert, welche nicht in Frage gestelltwerden sollten, dennoch kann man die meisten Vorgaben auf die jeweiligvorherrschenden Bedürfnisse anpassen und konfigurieren. So kann zum Beispieldie Lage des Projekt-Quellcodes sowie dessen Ressourcen konfiguriert werden,oder die Namen der resultierenden JAR-Archive angepasst werden. Durch dieEntwicklung von massgefertigten Plugins, kann fast jedes Verhalten an dieBedürfnisse Ihrer Umgebung angepasst werden. Sollten Sie der Konvention nichtfolgen wollen, ermöglicht Maven es Ihnen Voreinstellungen anzupassen um Ihrenspezifischen Anforderungen Rechnung zu tragen.

1.3. Die gemeinsame SchnittstelleIn der Zeit vor Maven, das eine gemeinsame Schnittstelle für den Build vonSoftware bereitstellte, hatte gewöhnlich jedes einzelne Projekt eine Person, welchesich der Verwaltung des völlig eigenen Build Systems widmete. Entwicklermussten einige Zeit aufwenden, um neben der tatsächlichen Entwicklungherauszufinden, welche Eigenheiten beim Build einer neuen Software zuberücksichtigen waren, zu welcher sie beitragen wollten. Im Jahr 2001, gab es

Einführung von Apache Maven

3

Page 23: Maven Definitive Guide De

völlig unterschiedliche Build Konzepte/Systeme welche für den Build einesProjekts wie Turbine, POI oder Tomcat zur Anwendung kamen. Jedesmal, wennein neues Source Code-Analyse-Tool zur statischen Analyse von Quellcodeveröffentlicht wurde, oder wenn jemand ein neues Unit-Test Framework herausgabwelches man einsetzen wollte/sollte, musste man alles stehen und liegen lassen, umherauszufinden, wie dieses neue Werkzeug in das Projekt und dessen BuildUmgebung einzupassen wäre. Wie konnte man Unit-Tests laufen lassen? Es gabtausend verschiedene Antworten. Dieses Umfeld war geprägt von tausendenendlosen Auseinandersetzungen bezüglich der 'richtigen' Werkzeuge undBuild-Vorgehen. Das Zeitalter vor Maven war ein Zeitalter der Ineffizienz: dasZeitalter der "Build-Ingenieure".

Heute setzen die meisten Open-Source-Entwickler bereits auf Maven, oder nutzendieses um neue Software-Projekten aufzusetzen. Dieser Übergang ist weniger einWechsel von einem Build Werkzeug zu einem anderen, sondern viel mehr eineEntwicklung, hin zum Einsatz einer gemeinsamen Build-Schnittstelle für Projekte.So wie Software-Systeme modularer wurden, wurden Build Werkzeuge komplexerund die Zahl der (Teil-)Projekte wuchs in den Himmel. In der Zeit vor Maven,mussten Sie, wollten Sie etwa ein Projekt wie Apache ActiveMQ oder ApacheServiceMix aus Subversion auschecken und aus den Quellen erstellen, mitmindestens einer Stunde Aufwand rechnen, die Sie damit verbrachtenherauszufinden, wie das Build-System des einzelnen Projekts funktionierte. Waswaren die Voraussetzungen, um das Projekt zu builden? Welche Bibliothekenmuss man herunterladen? Wo bekomme ich diese? Welche Targets kann ich imBuild ausführen? Im besten Fall dauerte es ein paar Minuten um herauszufindenwie ein neues Projekt zu builden war, - und im schlimmsten Fall (wie die alteServlet-API-Umsetzung des Jakarta-Projekts), war ein Projekt aufbauen soschwierig, das es mehrere Stunden dauerte nur um an einen Punkt zu gelangen, andem ein neuer Mitarbeiter den Quellcode bearbeiten konnte und das Projektkompilierte. Heutzutage machen Sie einen Projekt Check-Out und führen mvninstall aus.

Während Maven eine Reihe von Vorteilen einschließlich der Verwaltung vonAbhängigkeiten und Wiederverwendung von gemeinsamer Logik zum Build durchPlugins bietet, ist der Hauptgrund für den Erfolg der, dass es gelungen ist eine

Einführung von Apache Maven

4

Page 24: Maven Definitive Guide De

einheitliche Schnittstelle für den Build von Software bereitzustellen. Wenn Siesehen, dass ein Projekt wie Apache Wicket Maven verwendet, können Sie davonausgehen, dass Sie in der Lage sein werden, Projekt ohne weiten Aufwandauszuchecken und aus den Quellen mittels mvn zu bauen und zu installieren. Siewissen, wo der Zündschlüssel hinkommt, Sie wissen, dass das Gas-Pedal auf derrechten Seite und die Bremse auf der Linken ist.

1.4. Universelle Wiederverwendung durchMaven-PluginsDer Herzstück von Maven ist ziemlich dumm. Es weiss nicht viel mehr als ein paarXML-Dokumenten zu parsen sowie einen Lebenszyklus und ein paar Plugins zuverwalten. Maven wurde so konzipiert, dass die meiste Verantwortung auf eineAnzahl Maven-Plugins delegiert werden kann, welche den Lebenszyklus vonMaven beeinflussen sowie gewisse Goals erreichen können. Der Großteil derArbeit geschieht in Maven Goals. Hier passieren Dinge wie die Kompilierung vonQuellcode, der Paketierung von Bytecode, die Erstellung von Websites, und jedeandere Aufgabe die um einen Build zu erfüllen notwenig ist. Die MavenInstallation nach dem Download von Apache weiß noch nicht viel über diePaketierung eines WAR-Archivs oder dem Ausführen eines JUnit-Tests; der größteTeil der Intelligenz von Maven ist in den Plugins enthalten und diese beziehtMaven aus dem Maven-Repository. In der Tat, das erste Mal, als Sie einen Aufrufwie z.B. mvn install auf Ihrer neuen Maven Installation absetzten, holte sichMaven die meisten der Core Maven Plugins aus dem zentralen Maven-Repository.Das ist mehr als nur ein Trick, um die Download-Größe von Maven in derVerteilung zu beeinflussen, dieses Verhalten ist es, das es erlaubt, durch dieErweiterung eines Plugins die Fähigkeiten des Builds zu verändern. Die Tatsache,dass Maven sowohl die Abhängigkeiten wie auch die Plugins aus einemRemote-Repository lädt ermöglicht universelle Wiederverwendung vonBuild-Logik.

Das Maven Surefire Plugin ist das Plugin, welches für die Ausführung vonUnit-Tests verantwortlich zeichnet. Irgendwo zwischen Version 1.0 und der aktuell

Einführung von Apache Maven

5

Page 25: Maven Definitive Guide De

verbreiteten Version hat jemand beschlossen, dieses neben der Unterstützung vonJUnit auch um die Unterstützung für das TestNG Unit-Test Framework zuerweitern. Dies geschah in einer Art und Weise, dass die Abwärtskompatibilitäterhalten blieb. Für die Benutzer des Surefire Plugin im Einsatz von JUnit3 Testsveränderte sich nichts, weiterhin werden die Tests kompiliert und ausgeführt, wiedies zuvor der Fall war. Aber Sie erhalten eine neue Funktionalität, denn solltenSie nun Unit Tests nach dem TestNG Framework ausführen wollen, so haben Sienun auch diese Möglichkeit, dank der Bemühungen der Maintainer des SurefirePlugin. Das Plugin erhielt auch die Fähigkeit annotierte JUnit4 Unit Tests zuunterstützen. Alle diese Erweiterungen wurden bereitgestellt, ohne dass Sie Mavenaktiv aktualisieren, oder neue Software installieren mussten. Und ganz zentral, Siemussten nichts an Ihrem Projekt ändern, abgesehen von einer Versionsnummer fürein Plugin in einem POM.

Es ist dieser Mechanismus welche sich auf wesentlich mehr als nur das SurefirePlugin auswirkt: Projekte werden mittels einem Compiler Plugin kompiliert,mittels JAR-Plugin in JAR-Archive gepackt, es gibt es Plugins zum Erstellen vonBerichten, Plugins zur Ausführung von JRuby und Groovy-Code, sowie Pluginszum Veröffentlichen von Websites auf Remote-Servern. Maven hat gemeinsameAufgaben in Plug-Ins herausgelöst welche zentral gewartet sowie universelleingesetzt werden. Wann immer Veränderungen in einem Bereich des Buildensumgesetzt werden, wann immer ein neues Unit-Test Framework freigegeben oderein neues Werkzeug bereit gestellt wird. Sie sind nicht gezwungen, dieseÄnderungen in ihren Build einzupflegen um dies zu unterstützen. Sie profitierenvon der Tatsache, dass Plugins von einem zentral gewarteten Remote-Repositoryheruntergeladen werden. Das ist die wahre Bedeutung von universellerWiederverwendung durch Maven Plugins.

1.5. Konzeptionelles Modell eines "Projekts"Maven unterhält ein (abstraktes) Modell eines Projekts, Sie verarbeiten also nichtnur Quellcode Dateien in Binärdateien, Sie entwickeln zugleich eine Beschreibungdes Software-Projekts und ordnen diesem eine Reihe einzigartiger Koordinaten zu.Sie beschreiben die Attribute des Projekts: Wie ist die Projektlizenzierung

Einführung von Apache Maven

6

Page 26: Maven Definitive Guide De

geregelt? Wer entwickelt und trägt zum Projekt bei? Zu welchen anderen Projektenbestehen Abhängigkeiten? Maven ist mehr als nur ein "Build-Tool", es ist mehr alsnur eine Verbesserung von Werkzeugen wie make™ und Ant™; es ist einePlattform, umfasst eine neue Semantik bezüglich Software-Projekten undSoftware-Entwicklung. Diese Definition eines Modells für jedes Projektermöglicht Funktionen wie:

Dependency Management / Abhängigkeits VerwaltungDa ein Projekt einzigartig mittels einer Gruppenkennung (groupId),Artefaktenkennung (artifactId) und Version (version) identifiziert wird, ist nunmöglich, diese Koordinaten zur Abhängigkeitsverwaltung einzusetzen.

Remote-RepositoriesMit Blick auf das Abhängigkeitsmanagement ist es nun möglich diese imMaven Projekt Objekt Modell eingeführten Koordinaten einzusetzen um MavenRepositorien aufzubauen.

Universelle Wiederverwendung der Build-LogikPlugins werden ausgerichtet auf das Projekt Objekt Model (POM) gebaut, siesind nicht dafür ausgelegt auf bestimmte Dateien an bestimmten Ortenzuzugreifen. Alles wird in das Modell abstrahiert, Plugin-Konfiguration undAnpassung geschieht im Modell.

Tool Portabilität / IntegrationWerkzeuge wie Eclipse, NetBeans oder IntelliJ haben jetzt einen gemeinsamenOrt um auf Projektinformationen zuzugreifen. Vor dem Aufkommen vonMaven, gab es für jede IDE eine spezifische Art und Weise in welcher dieseDaten abgelegt wurden, was im Wesentlichen einem benutzerdefinierten POMentspricht. Maven standardisiert diese Beschreibung und während jeder IDEweiterhin deren eigenes Datenablagesystem unterhalten kann, lässt sich diesesnun leicht aus dem Modell heraus generieren.

Einfache Suche und Filterung von Projekt-ArtefaktenWerkzeuge wie Nexus ermöglichen es Ihnen schnell und einfach Archive aufder Basis der im POM enthaltenen Daten zu indexieren und zu durchsuchen.

Einführung von Apache Maven

7

Page 27: Maven Definitive Guide De

Maven hat eine Grundlage für die Anfänge einer konsistenten semantischenBeschreibung eines Software-Projekts geschaffen.

1.6. Ist Maven eine Alternative zu XYZ?Natürlich, Maven stellt eine Alternative zu Ant dar, aber Apache Ant ist nach wievor ein großartiges, weit verbreitetes Werkzeug. Es stellt seit Jahren denamtierende Champion der Java Build Tools, und Sie können auch weiterhinAnt-Build-Skripte einfach in Ihr Maven Projekt integrieren. Das ist auch einegebräuchliche Art, Maven einzusetzen. Andererseits, jetzt da mehr und mehrOpen-Source-Projekte sich auf Maven zubewegen und Maven als ProjektManagement Plattform einsetzen, haben aktive Entwickler begonnen zu erkennen,dass Maven nicht nur die Aufgabe der Build Verwaltung unterstützt, sonderninsgesamt zur Förderung einer gemeinsamen Schnittstelle zwischen Entwicklernund Software Projekte beiträgt. Maven hat viele Ausprägungen eine Plattform alsnur eines Werkzeugs. Wenn Sie die Auffassung vertreten würden, Maven als eineAlternative zu Ant anzusehen, so würden Sie Äpfel mit Birnen vergleichen. Mavenumfasst mehr als nur ein einen Werkzeugkasten um Projekte zu Builden.

Dies ist das zentrale Argument, welches alle Vergleiche der Art Maven/Ant,Maven/Buildr, Maven/Gradle unerheblich macht. Maven ist nicht bestimmt vonder Mechanik Ihres Build-Systems, es fusst nicht auf dem Skripting derverschiedenen Aufgaben Ihres Builds, sondern es geht weit mehr um dieFörderung einer Reihe von Standards, einer gemeinsamen Schnittstelle, einesLeben-Zykluses, einem Standard-Repository Format, einem Standard-VerzeichnisLayout, usw. Es geht sicherlich nicht darum, welches POM-Format zum Einsatzkommt, ob XML, YAML, Ruby oder Maven. Maven bietet weit mehr als dies:Maven und bezieht sich auf viel mehr als nur das Werkzeug. Wenn dieses Buchvon Maven spricht, so bezieht sich dies auf die Zusammenstellung von Software,Systemen und Standards, welche Maven unterstützt. Buildr, Ivy, Gradle alle dieseWerkzeuge interagieren mit den Repository-Format welches zu definieren Mavenbeigetragen hat. Sie könnten genauso einen Build auf der Basis von Buildr einzigunter Zuhilfenahme von einem Tool wie Nexus aufbauen, Nexus wird invorgestellt in Kapitel 16: Repository-Manager.

Einführung von Apache Maven

8

Page 28: Maven Definitive Guide De

Während Maven eine Alternative für viele dieser Tools darstellt, muss dieGemeinschaft der Open Source Entwickler darüber hinaus kommen können,Technologie als ein kontinuierliches Nullsummenspiel zwischen unfreundlichenKonkurrenten in einer kapitalistischen Wirtschaft zu sehen. Dies mag imWettbewerb von Großunternehmen und Ihrem Bezug zu einander sinnvollerscheinen, hat aber wenig Relevanz auf die Art und Weise, wieOpen-Source-Communities arbeiten. Die Überschrift "Wer gewinnt? Ant oderMaven?" ist nicht sehr konstruktiv. Zwingen Sie uns, diese Frage zu beantworten,werden wir uns definitiv auf die Seite von Maven schlagen und darlegen, dassMaven eine gegenüber Ant überlegene Alternative darstellt die darüberhinaus dieGrundlage für eine Technologie des Buildes legt. Gleichzeitig bitten wir zuberücksichtigen, dass die Grenzen von Maven in ständiger Bewegung sind, dieMaven Gemeinde ist immerzu versucht, neue Wege zu mehr Ökumene, mehrInteroperabilität, größerer Gemeinschaft zu schaffen. Die Kernkomponenten vonMaven sind der deklarative Build, die Abhängigkeitsverwaltung (DependencyManagement), Repository Verwaltung und breite Wiederverwendbarkeit durch denEinsatz von Plugins. Es sind aber die spezifische Inkarnationen dieser Ideen zueinem gegebenen Zeitpunkt weniger wichtig, als das Ziel dass dieOpen-Source-Community in Zusammenarbeit zur Verringerung der Ineffizienz von"Enterprise Scale Builds" beizutragen.

1.7. Ein Vergleich von Maven und AntWährend der vorangegangene Abschnitt Ihnen verständlich gemacht haben sollte,dass die Autoren dieses Buches keinerlei Interesse an der Schaffung oderVertiefung einer Fehde zwischen Apache Ant und Apache Maven haben, ist unsdie Tatsache bewusst, dass die meisten Organisationen eine Entscheidungzwischen Ant und Maven treffen (müssen). In diesem Abschnitt werden wir daherdiese beiden Werkzeuge gegenüberstellen.

Ant läuft beim Build-Prozess zu seiner vollen Grösse auf: Es ist ein Build-Systemnach dem Vorbild von make mit Targets und Abhängigkeiten. Jeder Target bestehtaus einer Reihe von Anweisungen, die in XML beschrieben werden. Es gibt eineTask copy, eine Task javac sowie eine Task JAR. Wenn Sie Ant benutzen, müssen

Einführung von Apache Maven

9

Page 29: Maven Definitive Guide De

Sie Ant mit der speziellen, spezifischen Anweisungen für die Zusammenstellungund dem Packaging Ihres Projektes aufrufen. Sehen Sie sich das folgende Beispieleiner einfachen Ant build.xml-Datei an:

Example 1.1. Eine einfache Ant build.xml-Datei

<project name="my-project" default="dist" basedir="."><description>

simple example build file</description>

<!-- set global properties for this build --><property name="src" location="src/main/java"/><property name="build" location="target/classes"/><property name="dist" location="target"/>

<target name="init"><!-- Create the time stamp --><tstamp/><!-- Create the build directory structure used by compile --><mkdir dir="${build}"/>

</target>

<target name="compile" depends="init"description="compile the source " >

<!-- Compile the java code from ${src} into ${build} --><javac srcdir="${src}" destdir="${build}"/>

</target>

<target name="dist" depends="compile"description="generate the distribution" >

<!-- Create the distribution directory --><mkdir dir="${dist}/lib"/>

<!-- Put everything in ${build} into the MyProject-${DSTAMP}.jar file --><jar jarfile="${dist}/lib/MyProject-${DSTAMP}.jar" basedir="${build}"/>

</target>

<target name="clean"description="clean up" >

<!-- Delete the ${build} and ${dist} directory trees --><delete dir="${build}"/><delete dir="${dist}"/>

</target></project>

An diesem einfachen Beispiel eines Ant Skriptes, können Sie sehen, wie genauman definieren muss, was man von Ant erwartet. Ein Target ist javac, hier werden

Einführung von Apache Maven

10

Page 30: Maven Definitive Guide De

die Quelldateien in Binärdaten verarbeitet, dabei ist es wichtig, dass man genauangibt, wo die Quell- sowie Zieldateien abgelegt werden (/src/main/java resp./target/classes). Ebenfalls müssen Sie Ant anweisen, aus den resultierendenDateien ein JAR-Archiv zu erstellen. Während einige der neueren Entwicklungendazu beitragen, dass Ant weniger prozedural orientiert arbeitet, ist dieEntwickler-Erfahrung dennoch die, einer in XML abgefassten prozeduralenProgrammiersprache.

Vergleichen Sie das Vorgehen von Maven mit dem vorigen Beispiel: In Maven,um ein JAR-Archive aus einer Reihe von Java Quellcode Dateien zu erstellen, istalles, was Sie tun müssen, ein einfaches POM (pom.xml Datei) zu generieren.Platzieren Sie den Quellcode in ${basedir}/src/main/java und führen Sie dannmvn install von der Befehlszeile aus . Das Beispiel der Maven pom.xml Datei,welches zum selben Ergebnis führt wie zuvor das Ant-Skript sehen Sie unten.

Example 1.2. Muster einer Maven pom.xml-Datei

<project><modelVersion>4.0.0</modelVersion><groupId>org.sonatype.mavenbook</groupId><artifactId>my-project</artifactId><version>1.0</version>

</project>

Das ist alles was Sie in der zugehörigen pom.xml Datei benötigen. Der Aufruf vonmvn install wird die Resourcen sowie Quelldateien kompilieren, bestehendeUnit-Tests ausführen, JAR-Archive erstellen und dieses in einem lokalenRepository anderen Projekten zur Verfügung stellen. Ohne die pom.xml Dateiabzuändern, können Sie durch den Aufruf von mvn site im Zielverzeichnis eineindex.html wiederfinden, welche den Link zu einer Dokumentations-Web-Seite aufder Basis von JavaDoc sowie einigen Standard Reports bereitstellt.

Zugegeben, dies ist das einfachste, mögliche Beispiel-Projekt. Ein Projekt, das nurQuellcode enthält und ein JAR-Archive erzeugt. Ein Projekt welches der MavenKonvention unterliegt und keinerlei Abhängigkeiten oder Anpassungenberücksichtigt. Sobald wir die Verhaltensweisen der Plugins anpassen wird unserepom.xml Datei wachsen. In den grössten Projekten werden Sie komplexe Maven

Einführung von Apache Maven

11

Page 31: Maven Definitive Guide De

POMs finden, welche eine grosse Anzahl Plugin-Anpassungen sowieAbhängigkeitserklärungen enthalten. Aber, selbst wenn das POM Ihres Projektserheblich wächst, enthält dies eine ganz andere Art von Daten und Informationendenn das Skript einer entsprechenden Ant-Datei. Maven POMs enthaltenDeklarationen wie: "Dies ist eine JAR-Projekt", und "Der Quellcode ist unter/src/main/java abgelegt". Wohingegen Ant-Build-Dateien explizite Anweisungenthalten: "Dies ist ein Projekt", "Die Quelle ist in /src/main/java", "javac gegendieses Verzeichnis ausführen", "Ablegen der Ergebnisse in /target/classses","Erstellen eines JAR-Archives aus der. ...". Wo Ant genauen Prozess-Anweisungenbedurfte, gab es etwas innerhalb des Maven Builds, das einfach "wusste", wo derQuellcode zu finden ist und wie dieser zu verarbeiten ist.

Die Unterschiede zwischen den Ant und Maven in diesem Beispiel sind:

Apache Ant

• Ant kennt keine formalen Konventionen bezüglich einer gemeinsamenProjekt Verzeichnis-Struktur. Sie müssen Ant genau sagen, wo sich dieQuelldateien befinden und wo die Ausgabe abgelegt werden soll. InformelleKonventionen haben sich im Laufe der Zeit herausgebildet, aber diesewurden nicht im Produkt kodifiziert.

• Ant ist Prozedural, Sie müssen Ant genau sagen, was zu tun ist und wanndies zu tun. Sie mussten definieren: erst kompilieren, dann kopieren, dannpaketieren.

• Ant kennt keinen Lebenszyklus, Sie mussten Targets definieren und derenAbhängigkeiten. Sie mussten die Abfolge der Schritte manuell für jedenTarget festlegen.

Apache Maven

• Maven arbeitet nach Konventionen. Da Sie diese beachteten, wusste Mavenwo Ihr Quellcode sich befand. Es stellt den Bytecode unter target/classesund es erzeugte ein JAR-Archive in /target.

Einführung von Apache Maven

12

Page 32: Maven Definitive Guide De

• Maven ist deklarative. Alles was Sie tun musste, war eine pom.xml Datei zuerzeugen sowie Ihre Quelle im Standard-Verzeichnis ablegen. Mavenkümmerte sich um den Rest.

• Maven kennt einen Lebenszyklus, welchen Sie mit dem Aufruf von mvninstall angestossen haben. Dieser Aufruf bringt Maven dazu, eine Abfolgevon Schritten abzuarbeiten, bis das Ende des Lebenszykluses erreicht ist. AlsNebeneffekt dieser Reise durch den Lebenszyklus führt Maven eine Reihevon Standard-Plugin-Goals aus, diese erledigen Arbeiten wie z.B.kompilieren oder dem Erstellen eines JAR-Archives.

Maven hat eine eingebaute Intelligenz bezüglich der gemeinsamenProjektaufgaben in Form von Maven-Plugins. Sollten Sie Unit Tests ausführenwollen, ist alles was Sie tun müssen, die Tests zu schreiben und unter${basedir}/src/test/java abzulegen, führen Sie eine Test bezogeneAbhängigkeit zu entweder TestNG oder JUnit ein, und starten Sie mvn test.Sollten Sie anstelle eines JAR-Archives eine Web Anwendung erstellen, so istalles was Sie tun müssen den Projekttyp auf "war" umstellen und IhrApplikationsverzeichnis (docroot) auf ${basedir}/src/main/webapp setzen. Klar,können Sie all dies auch mit Ant bewerkstelligen, aber Sie werden alleAnweisungen von Grund auf festhalten. In Ant würden Sie erst einmalherausfinden, wo die JUnit-JAR-Datei sich befindet, dann müssten Sie einenKlassenpfad erstellen welcher auch die JUnit-Archive Datei enthält, undanschliessend Ant mitteilen wo sich der Quellcode befindet. Schliesslich würdenSie einen Target erstellen die Quellen der Test Dateien zu kompilieren und zuguter Letzt die Tests mit JUnit auszuführen.

Ohne Unterstützung von Technologien wie antlibs und Ivy (und auch mit diesenunterstützenden Technologien) stellt sich bei Ant das Gefühl eines prozeduralenBuild Ablaufs ein. Eine effiziente Zusammenstellung einiger Maven POMs ineinem Projekt welches sich an die Maven-Konventionen hält erzeugt überraschendwenig XML im Vergleich zur Ant-Alternative. Ein weiterer Vorteil von Maven istdie Abhängigkeit von weithin benutzten Maven-Plugins. Heutzutage benutzt jederdas Maven Surefire Plugin für Unit-Tests, sobald jemand ein weiteres Unit TestFramework einbindet, können Sie neue Fähigkeiten in Ihrem eigenen Build durch

Einführung von Apache Maven

13

Page 33: Maven Definitive Guide De

die einfache Änderung der Version eines einzelnen, bestimmten Maven Pluginsnutzen.

Die Entscheidung über die Verwendung Maven oder Ant ist heutzutage nichtbinär. Ant hat noch immer einen Platz in in komplexen Builds. Wenn Ihr aktuellerBuild-Prozess einige sehr individuelle Prozesse enthält, oder wenn Sie etlicheAnt-Skripte geschrieben haben um einen bestimmten Prozess in einer bestimmtenWeise, die sich nicht an die Normen Maven hält, abzudecken, können Sie dieseSkripte dennoch mit Maven abarbeiten. Ant wird als Kernkomponente in Formeines Plugins von Maven zur Verfügung gestellt. Benuzerdefinierte Maven-Pluginskönnen in Ant geschrieben werden und Maven Projekte können so konfiguriertwerden, das Ant-Skripte innerhalb des Maven-Projekt-Lebenszykluses abgearbeitetwerden.

1.8. ZusammenfassungDiese Einführung wurde absichtlich kurz gehalten. Wir haben Ihnen umrissen, wasMaven ist und wie es zu einem guten Build-Prozess beiträgt, sowie über die Zeithinweg diesen verbessern hilft. Im nächste Kapitel werden wir am Beispiel eineseinfachen Projektes aufzeigen welche phänomenalen Aufgaben mit demkleinstmöglich Aufwand an Konfiguration geleistet werden können.

Einführung von Apache Maven

14

Page 34: Maven Definitive Guide De

Chapter 2. Installieren und Ausführen vonMavenDieses Kapitel enthält detaillierte Anweisungen für die Installation von Maven aufeiner Reihe von unterschiedlichen Plattformen. Statt ein bestimmtes Mass derVertrautheit mit der Installation von Software und dem Einstellen vonUmgebungsvariablen vorauszusetzen, haben wir uns entschieden, so detailliert wiemöglich zu sein, um Probleme welche von unvollständigen Installation herrührenvon vornherein zu minimieren. die einzige Voraussetzung welche angenommenwird ist die, einer bestehenden, geeigneten und vollständigen Java Umgebung,eines Java Development Kit (JDK). Sollten Sie nur Interesse an der Installationhaben, so können Sie nach dem Lesen der Abschnitte "2.2 Herunterladen vonMaven" sowie "2.3 Installation von Maven" zu den weiterführenden Kapiteln desBuches übergehen. Wenn Sie an einer detaillierten Beschreibung interessiert sind,so gibt Ihnen dieses Kapitel die entsprechenden Informationen sowie eineErklärung der Bedeutung der Apache Software License, Version 2.0.

2.1. Überprüfen der Java-InstallationObschon Maven auch auf Java 1.4 unterstützt ist, geht dieses Buch davon aus, dasssie mindestens auf Java 5 aufsetzen. Wählen Sie den neusten stabilen JDK welcherfür Ihre Plattform erhältlich ist. Alle in diesem Buch aufgeführten Beispiele sindunter Java 5 oder Java 6 lauffähig.

% java -versionjava version "1.6.0_02"Java(TM) SE Runtime Environment (build 1.6.0_02-b06)Java HotSpot(TM) Client VM (build 1.6.0_02-b06, mixed mode, sharing)

Maven ist unter allen Java-kompatibel zertifiziert Entwicklungsumgebungen,sowie einigen weiteren, nicht-zertifizierten Implementierungen von Java lauffähig.Die Beispiele in diesem Buch wurden gegen die offizielle Java Development KitImplementation wie diese von Sun Microsystems Webseite (http://java.sun.com)

15

Page 35: Maven Definitive Guide De

heruntergeladen werden kann, geschrieben und getestet. Sollten Sie mit einerLinux-Distribution arbeiten, müssen Sie eventuell die JavaEntwicklungsumgebung selbst herunterladen und installieren. Bitte stellen Siesicher, dass diese auch zum Einsatz kommt: durch Aufruf von "java -fullversion ".Nun, da Sun Microsystem den Quellcode von Java unter einer Open Source Lizenzoffengelegt hat, tritt diesbezüglich hoffentlich schon bald eine Besserung ein undes werden auch auf puristisch Linux-Distributionen die originalen Sun JRE sowieJDKs installiert. Bis zu diesem Tag müssen Sie selbst Hand anlegen, Javaherunterladen und installieren.

2.2. Herunterladen von MavenSie können Maven von der Apache Maven-Projekt-Website unterhttp://maven.apache.org/download.html herunterladen.

Beim Herunterladen von Maven, wählen Sie die neuste Version von ApacheMaven vom Website. Die aktuell neuste Version zur Zeit als dieses Buchgeschrieben wurde war Maven 2.0.9. Sollten Sie die Apache Software Lizenz nochnicht kennen, so machen sie sich - vor dem Einsatz des Produktes - mit derenBedingungen vertraut. Weitergehende Informationen diesbezüglich finden Sie inAbschnitt "2.8 Die Apache Software Lizenz".

2.3. Installation von MavenDie Unterschiede zwischen den Betriebssystemen wie Mac OS X und MicrosoftWindows sind gross, daneben gibt auch subtile Unterschiede zwischen denverschiedenen Versionen von Windows oder Varianten von Linux zu beachten.Zum Glück ist der Prozess der Installation von Maven auf all diesenBetriebssystemen relativ einfach und schmerzlos. Die folgenden Abschnitte gebeneinen Überblick über die empfohlene Best-Practice der Installation von Mavenunter einer Vielzahl von Betriebssystemen.

Installieren und Ausführen von Maven

16

Page 36: Maven Definitive Guide De

2.3.1. Maven Installation unter Mac OSXAm Anfang benötigen Sie zunächst die Software, diese bekommen Sie unter:http://maven.apache.org/download.html. Laden Sie die aktuelle Version vonMaven in einem für Sie passenden Format herunter. Wählen Sie einen geeignetenPlatz als "Heimat" und entpacken Sie das Archiv dort. Wenn Sie zum Beispiel dasVerzeichnis /usr/local/apache-maven-2.0.9 gewählt haben, so macht es Sinn einensymbolischen Link zu erzeugen. Dies erleichtert die Arbeit und erspart dieAnpassung von Umgebungsvariablen im Falle eines Upgrades der Version.

/usr/local % cd /usr/local/usr/local % ln -s apache-maven-2.0.9 maven/usr/local % export M2_HOME=/usr/local/maven/usr/local % export PATH=${M2_HOME}/bin:${PATH}

Ist Maven einmal installiert ist, müssen Sie noch ein paar Schritte unternehmen,damit Maven korrekt funktionieren kann. Zu aller erst sollten Sie dasbin-Verzeichnis Ihrer Maven Distribution (in diesem Beispiel/usr/local/Apache-Maven/bin) in den Befehlspfad aufnehmen. Ausserdemsollten Sie noch die Umgebungsvariable M2_HOME auf dasMaven-Top-Level-Verzeichnis setzen (in diesem Beispiel /usr/local/Maven).

NoteInstallations-Anweisungen sind die gleichen für OSX Tiger und OSXLeopard. Es wurde berichtet, dass Maven 2.0.6 mit einerVorschau-Version von XCode ausgeliefert wird. Sollten Sie XCodeinstalliert haben, führen Sie mvn von der Befehlszeile aus, undüberprüfen Sie die Verfügbarkeit. XCode installiert Maven unter/usr/share/Maven. Wir empfehlen die Installation der jüngsten Versionvon Maven 2.0.9, da hier eine Reihe von Bugfixes und Verbesserungeneingeflossen sind.

Um die Konfiguration beider Umgebungsvariablen bei jedem Start zu setzen, ist esnotwendig diese in einem Skript abzulegen welches bei jedem Systemstartabgearbeitet wird. Fügen Sie daher die folgenden Zeilen der Datei .bash_login an:

export M2_HOME=/usr/local/maven

Installieren und Ausführen von Maven

17

Page 37: Maven Definitive Guide De

export PATH=${M2_HOME}/bin:${PATH}

Mit dem Anfügen dieser Zeilen wird Maven Ihrer Umgebung zugefügt, und Siebekommen die Möglichkeit Maven von der Kommandozeile zu starten.

NoteDiese Installationsanleitung geht davon aus, dass Sie unter der bash-Shellarbeiten.

2.3.1.1. Maven Installation unter Mac OSX mit MacPortsSollten Sie MacPorts einsetzen, so können Sie Maven installieren, indem Sie diefolgenden Befehle auf der Kommandozeile eingeben:

$ sudo port install maven2Password: ******---> Fetching maven2---> Attempting to fetch apache-maven-2.0.9-bin.tar.bz2 from http://www.apache.org/dist/maven/binaries---> Verifying checksum(s) for maven2---> Extracting maven2---> Configuring maven2---> Building maven2 with target all---> Staging maven2 into destroot---> Installing maven2 2.0.9_0---> Activating maven2 2.0.9_0---> Cleaning maven2

Für weiterführende Informationen bezüglich der Maven 2 Portierung verweisenwir auf Portfile. Bezüglich weiterführender Informationen zu MacPorts und wiedies zu installieren ist: MacPorts Projekt Seite (Engl.).

2.3.2. Maven Installation unter Microsoft WindowsDie Installation von Maven unter Windows ist sehr ähnlich wie jene unter MacOSX, die wichtigsten Unterschiede finden sich bezüglich des Installationsortes undder Festlegung der Umgebungsvariablen. Dieses Buch geht von einem MavenInstallations-Verzeichnis von "C:\Program Files\Maven-2.0.9" aus, aber es wirdkeinen Unterschied machen, sollten Sie Maven in ein anderes Verzeichnisinstallieren, solange Sie die Umgebungsvariablen entsprechend anpassen. Nach

Installieren und Ausführen von Maven

18

Page 38: Maven Definitive Guide De

dem Entpacken des Maven Installationspaketes müssen Sie zweiUmgebungsvariablen - PATH und M2_HOME - setzen. Um dies zu bewerkstelligen,geben Sie auf der Kommandozeile folgende Befehlen ein:

C:\Users\tobrien > set M2_HOME=c:\Program Files\apache-maven-2.0.9C:\Users\tobrien > set PATH=%PATH%;%M2_HOME%\bin

Das Setzen der Umgebungsvariablen in der Kommando-Zeile erlaubt Ihnen dieAusführung von Maven während Ihrer aktuellen Sitzung und zwingt Sie diese beijedem Neustart erneut zu setzen. Sie sollten diese Variablen daher denSystemeinstellungen zufügen, öffnen Sie hierzu das Menue "Systemeinstellungen"von dort "System" und wählen Sie "".

2.3.3. Maven Installation unter LinuxZur Installation von Maven auf einer Linux-Maschine, folgen Sie derVorgehensweise in Abschnitt 2.3.2: "Installation von Maven auf Mac OSX".

2.3.4. Maven Installation unter FreeBSD oder OpenBSDZur Installation von Maven auf einem FreeBSD oder OpenBSD Maschine, folgenSie der Vorgehensweise in Abschnitt 2.3.2: "Installation von Maven auf MacOSX"

2.4. Testen einer Maven InstallationIst Maven erst einmal installiert, können Sie die Version überprüfen, indem Siemvn -v in der Befehlszeile eingeben. Bei einer korrekten Installation von Mavensollte eine Ausgabe wie folgt erscheinen:

$ mvn -vMaven 2.0.9

Sollten Sie diese Ausgabe erhalten, wissen Sie dass Maven verfügbar und bereit istum eingesetzt zu werden. Wenn nicht, hat Ihr Betriebssystem den mvn Befehlnicht gefunden. Überprüfen Sie ob die PATH-Umgebungsvariable sowie M2_HOME

Installieren und Ausführen von Maven

19

Page 39: Maven Definitive Guide De

1Haben Sie je eine 200-GB-Festplatte gekauft, nur um herauszufinden, dass diese weniger als 200 GiBfasst, wenn Sie diese installiert haben? Computer verstehen Gibibytes, aber Einzelhändler verkaufenProdukte mit Gigabyte. MiB steht für die Mebibyte und ist definiert als 2^20 oder 1024^2. Diese binärePräfix Standards sind von der IEEE, CIPM, und und IEC übernommen. Für weitere Informationen überKibibytes, Mebibytes, Gibibytes und Tebibytes siehe auch http://en.wikipedia.org/wiki/Mebibyte.

(Gross/Kleinschreibung beachten!) richtig gesetzt sind.

2.5. Spezielle InstallationshinweiseDas herunter zu ladende Maven Paket hat eine Grösse von etwa 1,5 MiB [1], dieseSchlankheit hat es erreicht, da der Kern von Maven mit dem Ziel entwickelt wurde,Plugins und Abhängigkeiten von einem Remote-Repository dynamisch und aufAbruf zuzuladen. Wenn Sie Anfangen mit Maven zu arbeiten, lädt Maven zunächstPlugins in ein lokales Repository. Dieses wird im Abschnitt 2.5.1"Benutzerdefinierte Konfiguration und -Repository" beschrieben. Im Fall, dass Sieneugierig sind, lassen Sie uns einen Blick darauf werfen, was sich im MavenInstallationsverzeichnis befindet. 1

/usr/local/maven $ ls -p1LICENSE.txtNOTICE.txtREADME.txtbin/boot/conf/lib/

Die Datei LICENSE.txt enthält die Software-Lizenz für Apache Maven. DieseLizenz ist in einigen Details in Abschnitt 2.8 "Über die Apache Software Lizenz"beschrieben. NOTICE.txt enthält einige Hinweise zu Abhängigkeiten vonBibliotheken, von denen Maven abhängig ist. README.txt enthältInstallationsanweisungen. Das Verzeichnis /bin enthält das "mvn"-Skript, mitwelchem Maven aufgerufen wird. /boot enthält eine JAR-Datei(classworlds-1.1.jar), welche den Class-Loader unter welchem Mavenausgeführt wird bereitstellt. /conf enthält eine Datei settings.xml, diese wirdeingesetzt um globale Einstellungen festzulegen um das Verhalten von Maven zudefinieren und anzupassen. Sollten Sie Maven Ihren Gegebenheiten anpassen

Installieren und Ausführen von Maven

20

Page 40: Maven Definitive Guide De

müssen, hat es sich herausgebildet, dass man die Einstellungen der settings.xmldurch eine Datei settings.xml im Verzeichnis ~/.m2 übersteuert. /lib enthälteine einzige JAR-Datei (Maven-2.0.9-uber.jar), welche dieHauptimplementierung (Core) von Maven enthält.

NoteMit der Ausnahme einer shared Unix Installation, sollten Sie auf dieAnpassung der settings.xml unter M2_HOME/conf vermeiden. DieAnpassung der globalen settings.xml-Datei der eigentlichen MavenInstallation ist gewöhnlich unnötig und erschwert den upgrade zwischenden Maven Installationen unnötigerweise, da Sie daran denken müssen,die angepasste settings.xml wieder von der bisherigen zur neuenMaven Installation zurückzukopieren. Sollten Sie dennoch gezwungensein, die settings.xml anzupassen, so sollten Sie die 'persönliche'settings.xml unter ~/.m2/settings.xml anpassen.

2.5.1. Benutzerdefinierte Konfiguration und-RepositorySobald Sie beginnen sich mit Maven ausgiebig auseinander zu setzen, werden Siefeststellen, dass Maven einige lokale, benutzerspezifische Konfigurationsdateienund ein lokales Archiv in Ihrem Home-Verzeichnis angelegt hat. Unter ~/.m2befinden sich:

~/.m2/settings.xmlEine Datei mit der benutzerspezifischen Konfiguration der Authentifizierung,Repositorien und anderen Informationen, um das Verhalten von Maven zusteuern.

~/.m2/repository/Dieses Verzeichnis enthält Ihr lokales Maven-Repository. Wenn Sie eineAbhängigkeit von einem Remote-Maven-Repository herunterladen, so wirddiese in ihrem lokalen Repository zwischengespeichert.

Installieren und Ausführen von Maven

21

Page 41: Maven Definitive Guide De

NoteUnter Unix (und OSX), bezeichnet die Tilde "~" Ihr Home-Verzeichnis(d.h. ~/bin bezieht sich auf /home/tobrien/bin). Unter Windows,werden wir uns ebenfalls mit ~ auf Ihr Home-Verzeichnis beziehen.Unter Windows XP ist Ihr Home-Verzeichnis C:\Dokumente und

Einstellungen\<Benutzername>, unter Windows Vista wird IhrHome-Verzeichnis mit C:\Users\<Benutzername> bezeichnet. ImFolgenden bitten wir Sie entsprechend Ihres Systems den Pfadanzupassen.

2.5.2. Aktualisieren einer Maven-InstallationSo Sie unter Unix/OSX arbeiten und die Installation wie oben unter Abschnitt2.3.2 "Installation von Maven auf Mac OSX" oder Abschnitt 2.3.3 "Installationvon Maven auf Linux" beschrieben ausgeführt haben, so ist es ein Kinderspiel eineMaveninstallation zu Aktualisieren: Installieren Sie einfach die neuere Version vonMaven (/usr/local/maven-2.future) neben der bestehenden Version von Maven(/usr/local/maven-2.0.9). Ändern Sie dann den symbolischen Link/usr/local/Maven von /usr/local/maven-2.0.9/ auf/usr/local/maven-2.future. Fertig! Da Ihre M2_HOME Variable bereits auf/usr/local/Maven zeigt, müssen keine weiteren Anpassungen ausgeführt werden.

Unter Windows, entpacken Sie einfach die neue Version von Maven unterC:\Program Files\Maven-2.future und aktualisieren Sie Ihre M2_HOME Variableüber die Systemsteuerungen.

NoteSollten Sie die globale Datei settings.xml von /M2_HOME/conf

angepasst haben, so müssen Sie diese Änderungen in das/conf-Verzeichnis der neuen Installation Übertragen.

Installieren und Ausführen von Maven

22

Page 42: Maven Definitive Guide De

2.5.3. Upgrade einer Maven-Installation von Maven 1.xauf Maven 2.xBeim Upgrade von<term>Maven 1</term>auf<term>Maven 2</term>werden Sie eine ganz neue POM Struktur sowie ein neues Repository benutzen.Haben Sie bereits ein benutzerspezifisches Maven 1 Repository fürmassgeschneiderte Artefakte erstellt, so können Sie mit Hilfe des NexusRepository Manager diese so darstellen, dass sie von Maven 2 Anwendungenbenutzt werden kann. Bezüglich weiterer Informationen zum Nexus RepositoryManager verweisen wir auf Kapitel 16: Repository Manager. Zusätzlich zumEinsatz von Werkzeugen wie Nexus, können Sie Referenzen auf Repositorien soeinstellen, dass diese das vorgängige Format unterstützen. Bezüglich weitererInformationen zum Konfiguration einer Referenz auf ein Maven 1 Repositorysiehe auch Abschnitt A.2.8: "Repositorien". Sollten Sie eine Anzahl bestehenderMaven 1 Projekte unterhalten, interessieren Sie sich bestimmt für das Maven OnePlugin. Das Plugin wurde entworfen, um Projekte von Maven 1 auf Maven 2 zuportieren. Ein bestehendes Maven 1 Projekt können sie portieren in dem Sie dasGoal one:convert wie folgt aufrufen:

$ cd my-project$ mvn one:convert

one:convert wird eine Maven 1 project.xml Datei einlesen und in einepom.xml-Datei konvertieren welches zu Maven 2 kompatibel ist. Sollten Sieallerdings ein Maven 1 Build Skript mittels Jelly angepasst haben, so müssen Sieandere Wege gehen. Während Maven 1 Jelly basierte Anpassungen favorisierte, istder bevorzugte Weg der Anpassung von Maven 2 Skripten mittelsbenutzerdefinierten Plugins, skriptbaren Plugins oder dem Maven Antrun Plugin.Das Wichtigste was es bei der Migration von Maven 1 auf Maven 2 zu beachtengilt ist, dass Maven 2 auf einem grundsätzlich verschiedenen Unterbau aufbaut.Maven 2 setzt auf Lebenszyklusphasen und definiert das Verhältnis zwischenPlugins auf eine andere Art und Weise. Sollten Sie eine derartige Migration

Installieren und Ausführen von Maven

23

Page 43: Maven Definitive Guide De

vornehmen, so müssen Sie Zeit einplanen, um sich mit den Unterschieden der zweiVersionen vertraut machen. Obschon es nahe liegt sich mit der neuen POMStruktur auseinanderzusetzen, sollten Sie sich zunächst auf dasLebenszykluskonzept konzentrieren. Sobald Sie das Lebenszykluskonzeptverstanden haben, steht Ihnen nichts mehr im Weg, Maven zur vollen Stärkeeinzusetzen.

2.6. Maven De-InstallierenDie meisten Installationsanleitungen für Maven beinhalten das Entpacken desMaven Bündel in ein Verzeichnis und das Setzen einiger Umgebungsvariablen.Sollten Sie also Maven von Ihrem Arbeitsplatz entfernen müssen, so müssen Sielediglich das Maven Installationsverzeichnis löschen und die Umgebungsvariablenentfernen. Sie sollten ebenfalls das Verzeichnis ~/.m2 entfernen, da dies Ihr lokalesRepository enthält.

2.7. Hilfe bekommen beim Arbeiten mit MavenWährend dieses Buch das Ziel hat, ein umfassendes Nachschlagewerk zu sein,wird es Themen geben welche wir verpasst haben oder besondere Umstände undSituationen sowie Tipps, welche nicht abgedeckt werden. Der Kern von Maven istsehr einfach, die eigentliche Arbeit geschieht in den Maven Plugins; und deren gibtes zu viele um alle in diesem Buch abdecken zu können. Sie werden zwangsläufigauf Schwierigkeiten stoßen und Eigenheiten finden, welche in diesem Buch nichtberücksichtigt wurden. In diesen Fällen empfehlen wir Ihnen die Suche nachAntworten an folgenden Orten:

http://maven.apache.orgDies sollte der erste Ausgangspunk einer jeden Suche sein. Der Maven-Websiteenthält eine Fülle von Informationen und Dokumentation. Jedes offiziellePlugin hat ein paar Seiten Dokumentation und es gibt eine Reihe von "QuickStart"-Dokumenten, die zusätzlich zum Inhalt dieses Buches hilfreich sein

Installieren und Ausführen von Maven

24

Page 44: Maven Definitive Guide De

werden. Während die Maven Website eine Fülle von Informationen enthält,kann es aber auch frustrierend, verwirrend und überwältigend sein, dort zusuchen. Die Seite enthält ein spezielles Google-Suchfeld welches diewichtigsten Informationsquellen bezüglich Maven durchsucht. DieseSuchmaske liefert bessere Ergebnisse als eine generische Google-Suche.

Maven User Mailing ListDie Maven User Mailing List ist der Ort für Nutzer um Fragen zu stellen. BevorSie sich mit einer Frage an die User Mailing List wenden, durchsuchen Sie bittevorangegangene Diskussionen zum betreffenden Thema. Es zeugt vonschlechtem Stil eine Frage stellen, die bereits zuvor gestellt wurde, ohne vorherzu prüfen, ob in den Archiven bereits eine Antwort schlummert. Es gibt eineReihe von nützlichen Mailing-List Archiv Browsern um Ihnen die Arbeit zuerleichtern. Für unsere Recherchen hat sich Nabble als am nützlichstenerwiesen. Sie können die User Mailing List Archive hier finden:http://www.nabble.com/Maven---Users-f178.html. Eine Beschreibung, wie Sieder User Mailingliste beitreten können finden sie hierhttp://maven.apache.org/mail-lists.html.

http://www.sonatype.comSonatype unterhält eine Online-Kopie dieses Buches und andere Tutorials imZusammenhang mit Apache Maven.

NoteTrotz der größten Bemühungen von einigen sehr engagierten MavenMitwirkenden, ist die Maven Website schlecht organisiert und vollerunvollständiger (und manchmal) irreführender Auszügen undDokumentationen. In der Gemeinschaft der Maven Entwickler gibt esleider einen Mangel gemeinsamer Standards der Plugin-Dokumentation.Einige Plugins sind sehr gut dokumentiert, während andere nicht einmaldie elementarsten Anweisungen für den Gebrauch ausweisen. Oft dererfolgversprechendste Ansatz die Suche nach einer Lösung in denArchiven der User Mailing Lists. Und wenn Sie wirklich helfen wollenunterbreiten Sie einen Patch der Maven Website (oder diesem Buch).

Installieren und Ausführen von Maven

25

Page 45: Maven Definitive Guide De

2.8. Das Maven Hilfe PluginIm Laufe des Buches werden wir Maven-Plugins Einführen, sprechen von MavenProject Object Model (POM)-Dateien, Einstellungen und Profilen. Es werdenZeiten kommen, in denen Sie ein Werkzeug benötigen, um Ihnen dabei zu helfen,den Sinn einiger in Maven genutzter Modelle zu erkennen sowie die Zielsetzungenbestimmter Plugins nachzuvollziehen. Das Maven Hilfe Plugin ermöglicht Ihnendie in Maven aktiven Profile auszugeben, das tatsächlich aktive Projekt ObjektModell (POM) anzusehen sowie die Attribute der Maven Plugins darzustellen.

NoteFür einen konzeptionellen Überblick über das POM und Plug-Insverweisen wir Sie auf Kapitel 3, Ein einfaches Maven-Projekt.

Das Hilfe-Maven Plugin hat vier Goals. Die ersten drei Goals: -active-profiles,effective-pom, sowie effective-settings beschreiben je ein bestimmtes Projekt undmüssen daher im Hauptverzeichnis des Projekts aufgerufen werden. Das letzteGoal - describe - ist etwas komplexer, es gibt Informationen über ein Plug-In oderdessen Zielsetzung aus.

help:active-profilesGibt die Profile (project, user, global) aus, welche für diesen Build aktiv sind

help:effective-pomZeigt das tatsächlich eintretende POM für den gegenwärtigen Build unterBerücksichtigung der aktiven Profile aus

help:effective-settingsGibt die Einstellungen des Projekts wieder, unter Berücksichtigung vonProfilerweiterungen sowie der Vererbung aus den globalen Einstellungen aufdie Benutzer spezifischen Einstellungen

help:describeBeschreibt die Attribute eines Plugins. Dieses Goal muss einzig nicht unter

Installieren und Ausführen von Maven

26

Page 46: Maven Definitive Guide De

einem bestehendes Projekt Verzeichnis aufgerufen werden. Es muss mindestensdie groupId und artifactId des Plugin welches Sie beschreiben wollengegeben werden

2.8.1. Beschreibung eines Maven PluginSobald Sie beginnen mit Maven zu arbeiten, werden Sie die meiste Zeit damitverbringen zu versuchen mehr Informationen zu Maven-Plugins zu bekommen:Wie funktionieren Plugins? Was sind die Konfigurations-Parameter? Was sind dieGoals? Das Goal help:describe werden Sie regelmässig benutzen um an dieseInformationen zu gelangen. Mit dem plugin-Parameter können Sie spezifizierenwelches Plugin Sie untersuchen möchten. Hierzu geben sie entweder dasPlugin-Prefix an (z.B. maven-help-plugin als help) oder diegroupId:artifact[:version], wobei die Version optional ist. hier ein Beispiel;der folgende Befehl verwendet das Hilfe Plugin Goal describe um Informationenüber das Maven Hilfe Plugin auszugeben.

$ mvn help:describe -Dplugin=help...Group Id: org.apache.maven.pluginsArtifact Id: maven-help-pluginVersion: 2.0.1Goal Prefix: helpDescription:

The Maven Help plugin provides goals aimed at helping to make sense out ofthe build environment. It includes the ability to view the effectivePOM and settings files, after inheritance and active profileshave been applied, as well as a describe a particular plugin goal to giveusage information.

...

Die Ausgabe des Goals describe mit dem Plugin-Parameter gibt die Koordinatendes Plugins aus, dessen Goal, Prefix und eine kurze Beschreibung. Obwohl dieseDaten hilfreich sind, werden Sie in der Regel ausführlichere Informationen suchen.Um dies zu erreichen, führen Sie das Goal help:describe mit dem Parameter fullaus, wie im folgenden beschrieben:

$ mvn help:describe -Dplugin=help -Dfull...Group Id: org.apache.maven.pluginsArtifact Id: maven-help-plugin

Installieren und Ausführen von Maven

27

Page 47: Maven Definitive Guide De

Version: 2.0.1Goal Prefix: helpDescription:

The Maven Help plugin provides goals aimed at helping to make sense out ofthe build environment. It includes the ability to view the effectivePOM and settings files, after inheritance and active profileshave been applied, as well as a describe a particular plugin goal togive usage information.

Mojos:

===============================================Goal: 'active-profiles'===============================================Description:

Lists the profiles which are currently active for this build.

Implementation: org.apache.maven.plugins.help.ActiveProfilesMojoLanguage: java

Parameters:-----------------------------------------------

[0] Name: outputType: java.io.FileRequired: falseDirectly editable: trueDescription:

This is an optional parameter for a file destination for the output ofthis mojo...the listing of active profiles per project.

-----------------------------------------------

[1] Name: projectsType: java.util.ListRequired: trueDirectly editable: falseDescription:

This is the list of projects currently slated to be built by Maven.

-----------------------------------------------

This mojo doesn't have any component requirements.===============================================

... removed the other goals ...

Diese Option ist ideal um alle Goals eines Plugins zu finden, sowie deren

Installieren und Ausführen von Maven

28

Page 48: Maven Definitive Guide De

Parameter. Aber manchmal ist dies weit mehr Informationen als notwendig. UmInformationen über ein einziges Goal zu erlangen, setzen Sie mit demPlugin-Parameter auch den mojo-Parameter zu. Der folgende Aufruf listet alleInformationen über das Goal compile des Compiler-Plugins.

$ mvn help:describe -Dplugin=compiler -Dmojo=compile -Dfull

NoteWas? Ein Mojo? In Maven, ein Plugin Goal ist bekannt als "Mojo".

2.9. Die Apache Software LizenzApache Maven ist unter der Apache Software Lizenz, Version 2.0 freigegeben.Wenn Sie die Lizenz lesen möchten, lesen Sie diese Lizenz abgelegt in${M2_HOME}/LICENSE oder lesen Sie diese auf der Webseiten derOpen-Source-Initiative http://www.opensource.org/licenses/apache2.0.php nach.

Es gibt eine gute Chance, dass Sie, wenn Sie dieses Buch lesen, kein Rechtsanwaltsind. Wenn Sie sich fragen, was die Apache Lizenz, Version 2.0 bedeutet, dann hatdie Apache Software Foundation hat eine sehr hilfreich "Häufig gestellte Fragen(FAQ)"- Seite bezüglich der Lizenz zusammengestellt. Sie finden diese unterhttp://www.apache.org/foundation/licence-FAQ.html. Da die FAQ in Englischgefasst sind, hier nur eine Beispielhafte Übersetzung der Frage "Ich bin kein Jurist.Was bedeutet das alles ?"

Die Apache Lizenz, Version 2.0, Erlaubt Ihnen

• die Apache Software kostenlos herunterladen und zu verwenden. Ganzoder teilweise, für persönliche, Firmen-interne oder kommerzielleNutzung einzusetzen.

• Apache Software in oder-Distributionen oder Pakete welche Sieerstellen einzusetzenVerbietet Ihnen:

Installieren und Ausführen von Maven

29

Page 49: Maven Definitive Guide De

• Apache Software oder davon abgeleitete Software ohne korrekteAngabe der Herkunft zu verbreiten;

• die Benutzung jeglicher Marken der Apache Software Stiftung in einersolchen Weise zu dass daraus abzuleiten ist, dass die Stiftung IhrProdukt unterstützt

• die Benutzung jeglicher Marken der Apache Software Stiftung in einersolchen Weise zu dass daraus abzuleiten ist, dass Sie die ApacheSoftware erschaffen haben.Zwingt Sie nicht

• die Source der Apache Software selbst, oder irgendwelcher davonabgeleiteten Produkte welche Sie daraus hergestellt haben einerDistribution oder einem Produkt welches Sie zusammengestellt habenbeizulegen.

• Änderungen welche sie an der Software der Apache Software Stiftungangebracht haben dieser zurückzugeben (Solcher Feedback wirdermutigt)

Installieren und Ausführen von Maven

30

Page 50: Maven Definitive Guide De

Part I. Maven by ExampleDas erste Buch zum Thema Maven welches erschienen ist, war das "MavenDeveloper's Notebook" von O'Reilly, dieses Buch, führte Maven in einer Reihevon einfachen Schritten ein. Die Idee hinter dem Developer's Notebook-Serie war,dass Entwickler am Besten lernen, wenn sie neben einem anderen Entwicklersitzen, und dem Denkprozess folgen, welcher ein anderer Entwickler beschreitetum sich eine neue Materie anzueignen und zu nutzen. Obschon die Serie derDeveloper's Notebooks erfolgreich war, gibt es eine entscheidende Kehrseite amNotebook-Format: Notebooks sind, par Definition, Ziel orientiert. Sie geleitendurch eine Abfolge von Schritten zu einem bestimmten Ziel. GrößereNachschlagewerke oder "Animal" Bücher sind dazu da, eine umfassendeMaterialsammlung über das gesamte Thema darzustellen. Beide Bücher habenVor-und Nachteile, aber die Veröffentlichung des einen ohne das andere ist einRezept für zukünftige Probleme.

Zur Verdeutlichung des Problems, nehmen Sie einmal an, dass zehntausendMenschen nachdem sie "A Developer's Notebook" gelesen haben, alle wissenwerden, wie man ein einfaches Projekt aufsetzt, dem Beispiel halber ein MavenProjekt welches aus einer Reihe von Quelldateien ein WAR-Archive generiert.Aber, sobald diese Personen tiefer gehende Informationen nachschlagen möchtenoder die eine oder andere Besonderheit zum Beispiel des "Assembly" Pluginserfahren wollen, stecken sie in so etwas wie einer Sackgasse. Da es kein gutgeschriebenes Referenzmanual für Maven gibt, müssen sie nun auf diePlugin-Dokumentation auf dem Maven Site zurückgreifen oder alternativ sichdurch eine Reihe von Mailinglisten quälen. Wenn die Leute erst einmal in derMaven Dokumentation graben, fangen sie an, tausende von schlecht geschriebenHTML-Dokumente des Maven Websites zu lesen, geschrieben von Hunderten vonverschiedenen Entwicklern, jeder mit einer anderen Vorstellung davon, was esbedeutet, ein Plugin zu dokumentieren: Hunderte von Entwicklern mitunterschiedlichen sprachlichen Stilen, Ausdrücken und Muttersprachen. Trotz dergrößten Bemühungen von Hunderten von gut meinenden Freiwilligen, ist dieLektüre der Plugin-Dokumentation auf der Website Maven, ist im besten Fallfrustrierend - und im schlimmsten Fall ein Grund Maven aufzugeben. Oftmals

31

Page 51: Maven Definitive Guide De

bleiben Maven Nutzer stecken, nur weil sie auf dem Maven Site schlicht keineAntwort auf ihr Problem finden können.

Während das erste Maven Developer's Notebook neue Benutzer zu Maven hinzog,und diese mit grundlegenden Kenntnissen zur Nutzung von Maven ausstattete,wuchs schon bald Frustration in dieser Gruppe, denn sie konnten beim bestenWillen kein prägnantes, gut geschriebenes Referenz-Handbuch finden. DieserMangel eines aussagekräftiges (oder definierenden) Referenz-Handbuches hatMaven für einige Jahre zurückgehalten; es wurde etwas wie eine dämpfende Kraftin der Maven User-Community. Dieses Buch zielt darauf ab, diese Situation zuändern, indem sie sowohl ein Update des ursprünglichen Maven EntwicklerNotebook in Teil I, "Maven by Example" darstellt, und auch den ersten Versucheines umfassenden Nachschlagewerks in Teil II, "Maven-Referenz". Was habenSie in Ihren Händen halten, (oder auf dem Bildschirm sehen) sind eigentlich zweiBücher in einem.

In diesem Teil des Buches, werden wir den erzählenden, fortschreitenden Stil desDeveloper's Notebooks beibehalten, denn es ist wertvolles Material, welches demEinsteiger hilft, anhand von Beispielen zu lernen, eben "Maven by Example". Inder ersten Hälfte des Buches wird Maven an Hand von Beispielen eingeführt, umdann in Teil II, der "Maven-Referenz" Lücken zu schliessen, uns in Details zuergeben und fortgeschrittene Themen einzuführen welche sonst unter Umständenden neuen Benutzer von Maven irritieren und vom Weg abbringen könnte.Während Teil II: Maven-Referenz auf einen Tabellenverweis oder einem vomBeispiel entkoppelten Programmausdruck setzt, setzt Teil I: Maven by Exampleauf die Kraft einer guten Vorbildwirkung sowie einer durchgehenden Storyline.Mit Abschluss von Teil I: Maven by Example, sollten Sie das Rüstzeug haben, dasSie brauchen, um mit Maven für ein paar Monate auszukommen. Wahrscheinlichwerden sie nur auf Teil II: Maven-Referenz zurückkommen, um eine Anpassungeines Maven-Plugins vorzunehmen, oder wenn Sie weitere Details zu deneinzelnen Plugins benötigen.

32

Page 52: Maven Definitive Guide De

Chapter 3. Ein einfaches Maven Projekt

3.1. EinleitungIn diesem Kapitel führen wir ein einfaches Projekt ein, welches wir von Grund aufmit dem Maven Archetype Plugin erstellen. Diese Anwendung bietet uns dieGelegenheit einige grundlegende Konzepte von Maven einzuführen, während Sieeinfach der Entwicklung des Projektes folgen.

Sollten Sie bereits zuvor Maven eingesetzt haben, werden Sie festgestellt haben,dass Maven mit sehr wenigen Details gut zurecht kommt. Ihr Build wird "einfachfunktionieren", und Sie brauchen sich nur um Details zu kümmern, wenn esnotwendig ist, das Standardverhalten zu ändern oder ein angepasstes Plugin zuerstellen. Allerdings, sollten Sie gezwungen sein sich um die Details von Maven zukümmern, so ist ein gutes Verständnis der grundlegenden Konzepte vonwesentlicher Bedeutung. Dieses Kapitel zielt darauf ab, das einfachste möglicheMaven-Projekt einzuführen und daran Ihnen einige der grundlegenden Konzepte,welche die Maven zu einer soliden Build Plattform machen, vorzustellen. Nachdem Lesen dieses Kapitels werden Sie ein grundlegendes Verständnis des BuildLifecycles, des Maven Repositories, der Abhängigkeits-Verwaltung (DependencyManagement) und dem Projekt Objekt Modell (POM) haben.

3.1.1. Das Herunterladen der Beispiele dieses KapitelsDieses Kapitel entwickelt ein sehr einfaches Beispiel, welches dazu dient einigeKern-Konzepte von Maven vorzustellen. Indem Sie den Schritten des Kapitelsfolgen, sollte es nicht notwendig werden die Beispiel herunterzuladen um den vonMaven erstellten Quellcode einzusehen. Um dieses Beispiel zu erstellen werdenwir das Archetype Maven Plugin einsetzen. Dieses Kapitel ändert das Projekt inkeiner Weise. Sollten Sie es vorziehen, dieses Kapitel anhand der letzendlichenQuellcode-Beispiele zu lesen, so kann das Beispielprojekt dieses Kapitelszusammen mit den anderen Beispielen dieses Buchs vonhttp://books.sonatype.com/maven-book/mvn-examples-1.0.zip oder

33

Page 53: Maven Definitive Guide De

http://books.sonatype.com/maven-book/mvn-examples-1.0.tar.gz heruntergeladenwerden. Entpacken Sie das Archiv in ein beliebiges Verzeichnis, und gehen Siedann zum Verzeichnis /ch03. Darin finden Sie ein Verzeichnis mit dem Namen/simple welches den Quellcode für dieses Kapitel enthält.

3.2. Erstellen eines einfachen ProjektsUm ein neues Projekt unter Maven zu beginnen, setzen Sie das ArchetypeMaven-Plugin von der Befehlszeile aus ein.

$ mvn archetype:create -DgroupId=org.sonatype.mavenbook.ch03 \-DartifactId=simple \-DpackageName=org.sonatype.mavenbook

[INFO] Scanning for projects...[INFO] Searching repository for plugin with prefix: 'archetype'.[INFO] artifact org.apache.maven.plugins:maven-archetype-plugin: checking for

updates from central[INFO] -----------------------------------------------------------------------[INFO] Building Maven Default Project[INFO] task-segment: [archetype:create] (aggregator-style)[INFO] --------------------------------------------------------------------[INFO] [archetype:create][INFO] artifact org.apache.maven.archetypes:maven-archetype-quickstart: \

checking for updates from central[INFO] Parameter: groupId, Value: org.sonatype.mavenbook.ch03[INFO] Parameter: packageName, Value: org.sonatype.mavenbook[INFO] Parameter: basedir, Value: /Users/tobrien/svnw/sonatype/examples[INFO] Parameter: package, Value: org.sonatype.mavenbook[INFO] Parameter: version, Value: 1.0-SNAPSHOT[INFO] Parameter: artifactId, Value: simple[INFO] * End of debug info from resources from generated POM *[INFO] Archetype created in dir: /Users/tobrien/svnw/sonatype/examples/simple

mvn ist der Aufruf von Maven2; archetype:create bezeichnet ein Maven Goal. SoSie Ant kennen, ist ein Maven Goal gleichbedeutend mit einem Ant Target. Beidebeschreiben eine Arbeitseinheit eines Builds. Die -Dname=wert Wertepaarestellen Argumente dar, welche an das Goal weitergereicht werden und nehmen dieForm von -D Eigenschaft an, ähnlich deren welche Sie der Java Virtual Machineüber die Befehlszeile weitergeben. Der Zweck des Goals archetype:create ist,schnell ein Projekt auf der Grundlage eines Archetypen zu erstellen. Ein Archetypin diesem Kontext ist definiert als "ein originalgetreues Modell oder Typmusternach dem andere, ähnliche Dinge gemustert sind, ein Prototyp" [1][2]. Von Maven

Ein einfaches Maven Projekt

34

Page 54: Maven Definitive Guide De

werden etliche Archetypen bereitgehalten, diese reichen von der einfachen SwingAnwendung bis hin zur komplexen Web-Applikation. In diesem Kapitel werdenwir den aller einfachsten Archtetypen nutzen um ein einfaches Gerüst einesStarter-Projekts zu erstellen. Das Plugin hat das Präfix "archetype" und dasentsprechende Goal ist "create". Sobald wir das Projekt generiert haben, sehen Siesich das Verzeichnis /simple und die von Maven darin generierteVerzeichnisstruktur näher an:

simple/❶simple/pom.xml❷

/src//src/main/❸

/main/java/src/test/❹

/test/java

Der erzeugte Verzeichnisbaum hält sich an das Maven Standard Layout, wirwerden später auf die Einzelheiten eingehen, aber für den Moment konzentrierenwir uns auf die wenigen grundsätzlichen Verzeichnisse:

❶ Das Archetype Maven Plugin erstellt ein Verzeichnis, das mit der artifactIdeinhergeht: /simple. Dieses Verzeichnis ist bekannt als das ProjektBasis-Verzeichnis.

❷ Jedes Maven-Projekt benötigt was als als Projekt Objekt Modell (POM)bekannt ist, in einer Datei mit dem Namen pom.xml. Diese Datei beschreibtdas Projekt, konfiguriert Plugins, und definiert Abhängigkeiten.

❸ Unser Projekt-Quellcode und zugehörige Ressourcen werden unter/src/main abgelegt. Im Falle unseres einfachen Java-Projekt wird dies ausein paar Java-Klassen und einigen Property-Dateien bestehen. In einemanderen Projekt könnte dies das Root-Dokument einer Web-Anwendung sein,oder Konfigurationsdateien für einen Applikations-Server. In einemJava-Projekt werden die Java-Klassen in /src/main/java sowie Ressoucenin /src/main/resources abgelegt.

❹ Unsere Projekt-Testfälle befinden sich unter /src/test. In diesemVerzeichnis werden Java-Klassen wie z.B. JUnit oder TestNG Tests(/src/test/java) abgelegt, sowie Klassenpfad relevante Ressourcen fürTests unter /src/test/resources.

Ein einfaches Maven Projekt

35

Page 55: Maven Definitive Guide De

Das Maven Archetype Plugin erzeugt eine einzige Klassecom.sonatype.maven.App. Dies ist eine dreizeilige Java-Klasse mit einerstatischen main()-Funktion welche eine Nachricht ausgibt:

package org.sonatype.mavenbook;

/*** Hello world!**/public class App{

public static void main( String[] args ){

System.out.println( "Hello World!" );}

}

Der einfachste Maven Archetyp generiert das einfachst mögliche Programm: einProgramm, welches "Hallo Welt!" auf der Standard-Ausgabe ausgibt.

3.3. Der Aufbau eines einfachen ProjektsSobald Sie das Projekt mit dem Maven Archetype Plugin erstellt haben, indem Siedie Anweisungen aus Abschnitt 3.2: Erstellen eines einfachen Projekts ausführen,werden Sie es builden und als Anwendung paketieren wollen. Um dies zubewerkstelligen, rufen Sie im Verzeichnis in welchem sich die pom.xml-Dateibefindet den Befehl mvn install von der Befehlszeile auf.

$ mvn install[INFO] Scanning for projects...[INFO] ----------------------------------------------------------------------[INFO] Building simple[INFO] task-segment: [install][INFO] ----------------------------------------------------------------------[INFO] [resources:resources][INFO] Using default encoding to copy filtered resources.[INFO] [compiler:compile][INFO] Compiling 1 source file to /simple/target/classes[INFO] [resources:testResources][INFO] Using default encoding to copy filtered resources.[INFO] [compiler:testCompile][INFO] Compiling 1 source file to /simple/target/test-classes[INFO] [surefire:test][INFO] Surefire report directory: /simple/target/surefire-reports

Ein einfaches Maven Projekt

36

Page 56: Maven Definitive Guide De

-------------------------------------------------------T E S T S-------------------------------------------------------Running org.sonatype.mavenbook.AppTestTests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.105 sec

Results :

Tests run: 1, Failures: 0, Errors: 0, Skipped: 0

[INFO] [jar:jar][INFO] Building jar: /simple/target/simple-1.0-SNAPSHOT.jar[INFO] [install:install][INFO] Installing /simple/target/simple-1.0-SNAPSHOT.jar to \

~/.m2/repository/com/sonatype/maven/ch03/simple/1.0-SNAPSHOT/ \simple-1.0-SNAPSHOT.jar

Sie haben soeben das einfachste mögliche Maven Projekt erstellt, kompiliert,getestet, paketiert und installiert. Um sich zu überzeugen, dass die Anwendungtatsächlich lauffähig ist, starten Sie sie von der Befehlszeile:

$ java -cp target/simple-1.0-SNAPSHOT.jar org.sonatype.mavenbook.AppHello World!

3.4. Einfaches Projekt Objekt Modell (POM)Bei der Ausführung von Maven richtet dieses sich nach dem Projekt ObjektModell (POM), um an Informationen über das Projekt zu gelangen. Das POMliefert Antworten auf Fragen wie: Welche Art von Projekte ist es? Wie lautet derProjektname? Gibt es irgendwelche Anpassungen am Build bezüglich diesemProjekt? Hier die grundlegende pom.xml-Datei wie sie vom Archetype MavenPlugin Goal create erstellt wurde.

Example 3.1. Einfachstes Projekt pom.xml Datei

<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.0http://maven.apache.org/maven-v4_0_0.xsd">

<modelVersion>4.0.0</modelVersion><groupId>org.sonatype.mavenbook.ch03</groupId><artifactId>simple</artifactId>

Ein einfaches Maven Projekt

37

Page 57: Maven Definitive Guide De

<packaging>jar</packaging><version>1.0-SNAPSHOT</version><name>simple</name><url>http://maven.apache.org</url><dependencies>

<dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>3.8.1</version><scope>test</scope>

</dependency></dependencies>

</project>

Diese pom.xml-Datei stellt das elementarste POM dar mit welchem Sie je in einemMaven Projekt zu tun haben werden. In der Regel ist eine POM-Datei wesentlichkomplexer: Abhängigkeiten werden definiert und Plugin Verhalten angepasst. Dieersten Elemente- groupId, artifactId, packaging sowie version sind alsMaven-Koordinaten bekannt. Sie sind der eindeutigen Identifizierung einesProjekts gewidmet. name und url sind beschreibende Elemente des POM, welchein einer einfach lesbare Art den Namen des Projektes sowie einer zugeordnetenProjekt-Website wiedergeben. Zuletzt wird im dependency Element eine einzigeBeziehung als "Test-Scoped" Abhängigkeit zum JUnit-Test Framework unter demNamen JUnit definiert. Diese Themen werden im Abschnitt 3.5: "Kernkonzepte"ausgeführt. Zum jetzigen Zeitpunkt ist alles was sie wissen müssen, dass diepom.xml-Datei Maven zum Leben erweckt.

Bei der Ausführung von Maven wird dieses gesteuert durch eine Kombination vonEinstellungen innerhalb der pom.xml-Datei, einer Art "Super"-POM welche sichim Installationsverzeichnis von Maven befindet, sowie (möglicherweise) einigenbenutzerspezifischen Einstellungen. Um sich das "tatsächliche" POM, oder dasPOM, gegen welches Maven ausgeführt wird anzusehen, geben Sie imStammverzeichnis Ihres Projekts den folgenden Befehl ein:

$ mvn help:effective-pom

Mit der Ausführung bekommen Sie ein wesentlich umfangreicheres POM, dasauch die Maven Standardeinstellungen enthält

Ein einfaches Maven Projekt

38

Page 58: Maven Definitive Guide De

3.5. Kern KonzepteNachdem Sie nun zum ersten Mal Maven eingesetzt haben, ist ein guter Zeitpunktgekommen einige der Kernkonzepte von Maven einzuführen. Im vorigen Beispielhaben Sie, aufbauend auf ein einfaches POM, ein einfaches Projekt generiert,welches im Standard Layout von Maven (der Konvention folgend) strukturiert dieQuelldateien abgelegt hat. Sie haben dann Maven mit einer Lebenszyklusphase alsArgument aufgerufen und damit ausgelöst, dass Maven eine Anzahl Plugin Goalsabgearbeitet hat. Zuletzt haben Sie einen Maven Artifact in Ihr lokales Repositoryinstalliert. Moment! Was ist ein "Lebenszyklus"? Was ist ein "lokales Repository"?Die folgenden Abschnitte beschreiben einige der für Maven zentralen Konzepte.

3.5.1. Maven Plugins und ZieleIm vorigen Abschnitt haben wir Maven mit zwei unterschiedlichenKommandozeilen-Argumenten aufgerufen. Der erste Befehl war ein einzigesPlugin Goal, das Goal create des Archetype Maven Plugin. Der zweite Aufruf vonMaven war hingegen gekoppelt mit einer Lifecycle-Phase - install. Um eineinzelnes (bestimmtes) Maven Plugin Goal auszuführen, benutzten wir die Syntaxmvn archetype:create, hierbei ist archetype die Kennung eines Plugins undcreate die Kennung eines Goals. Wann immer Maven ein Plugin Goal ausführt,gibt es die Plugin- sowie die Goal-Kennung auf der Standardausgabe aus:

$ mvn archetype:create -DgroupId=org.sonatype.mavenbook.ch03 \-DartifactId=simple \-DpackageName=org.sonatype.mavenbook

...[INFO] [archetype:create][INFO] artifact org.apache.maven.archetypes:maven-archetype-quickstart: \

checking for updates from central...

Ein Maven-Plugin ist eine Sammlung von einem oder mehreren Goals. Beispielevon Maven-Plugins sind einfache Core-Plugins wie das JAR-Plugin welches dasGoal zur Erzeugung von JAR-Archiven beinhaltet; das Compiler-Plugin mit denGoals Source Code kompilieren oder Unit Test Quellcode zu kompilieren oder dasSurefire Plugin welches Goals zum Abarbeiten von Unit Tests sowie der Report

Ein einfaches Maven Projekt

39

Page 59: Maven Definitive Guide De

Generierung diesbezüglich beinhaltet. Andere, stärker spezialisierteMaven-Plugins sind z. B. das Hibernate3 Plugin zur Integration mit der beliebtenHibernate Persistenz Bibliothek, dem JRuby Plugin, welches erlaubt Ruby als Teildes Builds auszuführen sowie Maven Plugins in Ruby zu schreiben. Maven siehtauch vor benutzerdefinierte Plugins einzubinden. Ein benutzerdefiniertes Pluginkann in Java geschrieben sein, oder aber auch in einer von vielen anderen Sprachendarunter Ant, Groovy, BeanShell, und, wie bereits erwähnt, Ruby.

Figure 3.1. Ein Plugin enthält Goals

Ein Goal ist eine spezifische Aufgabe, welche alleinstehend, oder zusammen mitanderen Goals als Teil eines größeren Builds ausgeführt werden kann. Ein Goal isteine "Arbeitseinheit" in Maven. Beispiele von Goals sind das Goal compile desCompiler-Plugin, es kompiliert den gesamten Quellcode für ein Projekt, oder dasGoal test des Surefire Plugin welches Unit-Tests ausführen kann. Goals werdendurch Konfigurations-Parameter bestimmt, welche erlauben das Verhaltenanzupassen. Ein Beispiel, das Goal compile des Compiler-Plugin definiert eineReihe von Konfigurations-Parameter, mit denen Sie die gewünschte JDK-VersionVersion bestimmen, sowie über den Einsatz von Compiler-Optimierungenbestimmen können. Im vorigen Beispiel haben wir über die Befehlszeile dieParameter groupId und artifactId an das Goal create des Archetype Pluginweitergegeben (-DgroupId=com.sonatype.maven.ch03 sowie-DartifactId=simple). Darüber hinaus haben wir den Parameter packageName alscom.sonatype.maven an das Goal create weitergegeben. Hätten wir den ParameterpackageName ausgelassen, wäre die Voreinstellung com.sonatype.maven.ch03

Ein einfaches Maven Projekt

40

Page 60: Maven Definitive Guide De

zum Zug gekommen.

NoteIm weiteren Text werden wir häufig, wenn wir auf ein Plugin Goalverweisen eine verkürzte Notation benutzen: pluginId:goalId; EinBeispiel: um sich auf das Goal create des Archetype Maven Plugin zubeziehen schreiben wir archetype:create.

Goals legen Parameter fest, für welche vernünftige Standardwerte definiert seinkönnen. Im vorangehenden Beispiel archtetype:create, wurde der Archetypenicht spezifiziert, es wurde lediglich eine groupId sowie eine artifactId

weitergegeben. Hier kommen wir das erste mal mit Konvention vor Konfigurationin Berührung. Die Konvention, oder der Standard, des Goals create ist dieGenerierung eines einfachen Projektes namens Quickstart. Das Goal createdefiniert einen Parameter archetype:artifactId welcher auf den Standardwertmaven-archetype-quickstart gesetzt ist. Der Archetyp 'Quickstart' erzeugt eineminimale Projekthülse, mit zugehörigem POM sowie einer einzigen Klasse. DasArchetype Plugin ist wesentlich mächtiger als dieses Beispiel andeutet, jedoch istes eine gute Möglichkeit, um neue Projekte schnell zu starten. Später in diesemBuch, zeigen wir Ihnen, wie das Archetype Plugin benutzt werden kann, umkomplexere Projekte wie Web-Anwendungen zu generieren, und wie Sie dasArchetype Plugin benutzen können um Ihren eigenen Satz von Projekten zudefinieren.

Der Kern von Maven hat nur wenig zu tun mit den spezifischen Aufgaben welcheam Build Ihres Projektes beteiligt sind. Von sich aus weiss Maven nicht wie manIhre Quelldateien kompiliert oder gar ein JAR-Archive packt. Maven delegiert alldiese Arbeiten an Maven-Plugins wie das Compiler Plugin oder das JAR-Plugin.Diese können, sobald sie gebraucht werden, aus dem zentralen Maven Repositoryheruntergeladen und periodisch aktualisiert werden. Wenn Sie Mavenherunterladen, so bekommen Sie lediglich den Kern oder Core von Maven. Dieserbesteht aus einer sehr grundlegenden Shell, welche nur in der Lage ist dieBefehlszeile zu analysieren, den Klassenpfad zu verwalten, eine POM-Datei zuanalysieren und nach Bedarf Plugins herunterzuladen. Indem das Compiler Plugin

Ein einfaches Maven Projekt

41

Page 61: Maven Definitive Guide De

vom Core getrennt gehalten wurde und über einen Update Mechanismus verfügt,ist es ein Leichtes für den Benutzer, auch die neusten Compiler Optionen zumEinsatz zu bringen. Auf diese Weise ermöglichen Maven-Plugins universelleWiederverwendbarkeit von übergreifender Buildlogik, Sie definieren nicht einenKompilierungs-Task innerhalb des Builds, Sie benutzen das Compiler Plugin wiees von jedem Maven Benutzer eingesetzt wird. Wird das Plugin weiterentwickelt,kann jedes Projekt auf der Basis von Maven sofort den Nutzen daraus ziehen (undsollten Sie das Compiler Plugin nicht mögen, so können Sie es übersteuern und Ihreigenes zum Einsatz bringen!).

3.5.2. Maven LifecycleDer zweite Aufruf des vorangegangenen Abschnitts war mvn package. Bei diesemAufruf wurde kein Plugin Goal angegeben, statt dessen wurde eineLebenszyklusphase angegeben. Eine Phase ist ein Schritt in dem was Maven den"Build Lifecycle" nennt. Der Build Lebenszyklus ist eine geordnete Folge vonPhasen welche am Aufbau eines Projekts beteiligt sind. Maven unterstützen kanneine Reihe ganz unterschiedlicher Lebenszyklen. Der am häufigsten zum Einsatzkommende ist der Standard-Lebenszyklus. Dieser beginnt mit eine Phase derValidierung der grundlegenden Integrität des Projekts und endet mit einer Phaseder Bereitstellung von einem Projekt für die Produktion. Lifecycle Phasen sindbewusst vage gehalten, im einzelnen lediglich definiert als Validierung, Test oderVerteilung kann eine Phase für verschiedene Projekte ganz unterschiedlicheBedeutung haben. Zur Verdeutlichung ein Beispiel: die Phase package bedeutet ineinem Projekt welches ein JAR-Archive erstellt: 'erstelle ein JAR-Archive',hingegen in einem Projekt welches eine Webanwendung zum Ziel hat: 'Erstelle einWAR-Archive'. Abbildung 3.2 "Ein Lebenszyklus besteht aus einer Abfolge vonPhasen" zeigt eine vereinfachte Darstellung des Maven Standard Lebenszykluses.

Plugin-Goals können an Lebenszyklus-Phasen gebunden werden. Mit demDurchschreiten des Lebenszykluses arbeitet Maven die Goals der einzelnen Phasenab. An jeder Phase können keine (0) oder mehrere Goals gebunden sein. Imvorangegangenen Abschnitt, beim Aufruf von mvn package, haben Sie vielleichtbemerkt, dass mehr als ein Ziel ausgeführt wurde. Sehen Sie sich die Ausgabe nach

Ein einfaches Maven Projekt

42

Page 62: Maven Definitive Guide De

dem Ausführen von mvn package genau an, Sie werden sehen, dass verschiedeneGoals ausgeführt wurden. Als dieses einfache Beispiel die Phase package erreichte,führte es das Goal jar des Jar Plugins aus. Da unser einfaches Schnellstart-Projekt(standardmäßig) den Paketierungstyp jar setzt, wird das Goal jar:jar an die Phasepackage gebunden.

Figure 3.2. Ein Goal wird an eine Phase gebunden

Wir wissen nun, dass die Phase package für ein Projekt mit jar-Paketierung einJAR-Archive erstellen wird. Aber wie steht es um die vorangehenden Goals, derenwie etwa compiler:compile und surefire:test? Diese Goals werden von Mavenbeim Durchschreiten der vorhergehenden Phasen des Lebenszykluses abgearbeitet.Der Aufruf einer bestimmten Phase löst aus, dass alle vorgängigen Phasen inentsprechender Reihenfolge abgearbeitet werden. Mit dem Abschluss derspezifizierten Phase endet die Bearbeitung. Jede Phase entspricht keinem odermehreren abzuarbeitenden Goals, und da wir keine weitere Plugin-Konfigurationoder Anpassung durchgeführt haben, bindet dieses Beispiel lediglich eine Reihevon Standard-Plugin-Goals an den Standard-Lebenszyklus. Läuft Maven entlangdes definierten Standard-Lebenszykluses bis zur Phase package, werden diefolgenden Goals in der angegebenen Reihenfolge ausgeführt:

resources:resources

Das Goal resources des Resouces Plugin wird an die Phase resources gebunden.Dieses Goal kopiert alle Ressourcen von /src/main/resources und allenanderen, konfigurierten Ressource- Verzeichnissen in dasAusgabe-Verzeichnis.

compiler:compile

Ein einfaches Maven Projekt

43

Page 63: Maven Definitive Guide De

Das Goal compile des Compiler Plugin wird an die Phase compile gebunden.Dieses Ziel kompiliert den gesamten Quellcode von /src/main/java oderjedem anderen konfigurierten Quellen-Verzeichnisse in dasAusgabe-Verzeichnis.

resources:testResources

Das Goal testResources des Resources Plugin wird an die Phase test-resourcesgebunden. Dieses Goal kopiert alle Ressourcen von /src/test/resources undjedem anderen, konfigurierten Test-Ressource Verzeichnis in ein Test AusgabeVerzeichnis.

compiler:testCompile

Das Goal testCompile des Compiler-Plugin wird an die Phase test-compilegebunden. Dieses Goal kompiliert die Testfälle von /src/test/java und allenanderen konfigurierten Test-Quellen-Verzeichnissen in ein Test AusgabeVerzeichnis.

surefire:test

Das Goal test des Surefire Plugin wird an die Phase test gebunden. Dieses Zielführt alle Tests aus und generiert Ausgabedateien der detaillierten Ergebnissen.Standardmäßig wird dieses Goal den Build beenden, sollte ein Test scheitern.

jar:jar

Das Goal jar des JAR-Plugin wird an die Phase package gebunden. Dieses Goalpaketiert das Ausgabeverzeichnis und erstellt ein JAR-Archiv.

Ein einfaches Maven Projekt

44

Page 64: Maven Definitive Guide De

Ein einfaches Maven Projekt

45

Page 65: Maven Definitive Guide De

Figure 3.3. Gebundene Goals werden mit den zugehörigen Phasen ausgeführt.(Anmerkung: Es bestehen mehr Phasen als oben abgebildet, dies ist einAuszug der Möglichkeiten)

Wir fassen zusammen: beim Ausführen von mvn package durchläuft Maven alleLebenszyklus Phasen bis und mit package, und führt hierbei in Abfolge alle Goalsaus, welche zugehörig an Phasen gebunden sind. Anstelle der Angabe eines MavenLifecycle Goals, können Sie das gleiche Ergebnis erreichen, indem Sie eineAbfolge von Plugin Goals angeben:

mvn resources:resources \compiler:compile \resources:testResources \compiler:testCompile \surefire:test \jar:jar \install:install

Die Ausführung der Phase package ist der Verwaltung aller beteiligten Goals einesbestimmten Builds vorzuziehen. Ein derartiges Vorgehen erlaubt den Projektenauch die Einhaltung genau definierter Standards. Es ist der Lebenszyklus, welchereinem Entwickler erlaubt von von einem Maven-Projekt zu einem anderen zuspringen, ohne sich um die Details der einzelnen Builds zu kümmern. Schaffen Siees ein Maven-Projekt zu builden, so schaffen Sie dies für alle!

3.5.3. Maven KoordinatenDas Archetype Plugin erstellte ein Projekt mit einer Datei namens pom.xml. Diesist das Projekt Objekt Modell (POM), eine deklarative Beschreibung einesProjekts. Wenn Maven ein Goal ausführt hat jedes Goal Zugang zu den im ProjektPOM definierten Informationen. Wenn das Goal jar:jar ein JAR-Archive erstellensoll, sieht es in der POM Datei nach um herauszufinden wie das JAR-Archiveheissen soll. Soll das Goal compiler:compiler Java Quelldateien kompilieren greiftdas Goal auf die POM Dateien zurück, um zu sehen ob dort CompilerEigenschaften definiert sind. Goals werden immer im Kontext eines POMsausgeführt. Goals sind Aktionen welche wir auf ein Projekt anwenden möchten,

Ein einfaches Maven Projekt

46

Page 66: Maven Definitive Guide De

und Projekte sind in POM Dateien abgebildet. Das POM bezeichnet ein Projekt,stellt einen Satz von Identifikationsmerkmale (Koordinaten) für ein Projekt, unddefiniert die Beziehungen zwischen diesem Projekt und andern Projekten durch dieAngabe von Abhängigkeiten, Eltern und Voraussetzungen. Ein POM kann auchPlugin-Verhalten beeinflussen sowie Informationen über die Entwicklergemeindeund an einem Projekt beteiligten Entwicklern bereitstellen.

Maven Koordinaten stellen eine Anzahl Identifikatoren, welche zur eindeutigenIdentifikation eines Projekts, einer Abhängigkeit oder eines Maven Plugins genutztwerden können. Werfen Sie einen Blick auf das folgende POM.

Figure 3.4. Maven Projekt Koordinaten

Wir haben die Maven Koordinaten für dieses Projekt: groupId, artifactId,version und packaging vorgestellt. Die Einheit dieser Identifikatoren bildet dieKoordinaten eines Projekts[3]. Genau wie in jedem anderen Koordinatensystem,fixieren diese Koordinaten einen bestimmten Punkt im Raum: vom Allgemeinenzum Spezifischen. Maven bestimmt ein Projekt mit dessen Koordinaten, so sich ein

Ein einfaches Maven Projekt

47

Page 67: Maven Definitive Guide De

1Es gibt eine weitere, fünfte Koordinate: classifier. Diese kommt nur selten zum Einsatz und wirwerden die Einführung daher auf später im Buch zurückstellen. Nehmen Sie sich die Freiheit dieseKoordinate im Moment zu ignorieren.

Projekt auf ein anderes bezieht, entweder als Abhängigkeit, Plugin oder auchelterliche Projekt Referenz. Maven Koordinaten werden gewöhnlich durch einenDoppelpunkt getrennt in folgender Form wiedergegeben:groupId:artifactId:version:packaging. Die Koordinate des im obigen Beispieleiner pom.xml-Datei eines Projektes sind daher folgendermassen wiedergegeben:mavenbook:my-app:jar:1.0-SNAPSHOT. Diese Notation gilt auch fürProjekt-Abhängigkeiten; unser Projekt basiert auf JUnit Version 3.8.1, es enthältdaher eine Abhängigkeit zu junit:junit:jar:3.8.1 . 1

groupId

Die "Gruppe" (Firma, Team, Organisation, Projekt, oder anderweitige Gruppe).Es gilt die ungeschriebene Regel, dass die groupId in derReverse-Domain-Name Notation der Organisation die das Projekt erstellt,wiedergegeben wird. Projekte von Sonatype hätte eine groupId welche mitcom.sonatype beginnt, Projekte der Apache Software Foundation hätte einegroupId welche mit org.apache beginnt.

artifactId

Eine eindeutige hierarchische Kennung unter der groupId, identifiziert eineinzelnes Projekt.

version

Eine bestimmte Version eines Projekts. Freigegebene Projekte haben einebestimmte, festgelegte Versionskennung. Diese bezieht sich immer auf genaueine Version des Projekts. Projekte in der aktiven Entwicklung können einespezielle Kennung bekommen, diese markiert die angegebene Version als eine"Momentaufnahme".

Der Identifikator packaging ist ebenfalls ein wichtiger Bestandteil der MavenKoordinaten, aber nicht Teil der eindeutigen Projektkennung. EineProjektdarstellung der Art groupId:artifactId:version bezeichnet ein Projekteinzigartig (kanonisch). Es ist nicht möglich ein weiteres Projekt mit dem gleichen

Ein einfaches Maven Projekt

48

Page 68: Maven Definitive Guide De

Bezeichnern zu erstellen.

packaging

Die Art in welcher das Projekt 'gepackt' ist, dies ist auf den Standardwert 'jar'gesetzt. Ein Projekt vom Packaging Typ 'jar' wird als JAR-Archivebereitgestellt, eines des Types 'war' in Form eines WAR-Archives.

Diese vier Elemente bestimmen den Schlüssel um ein Projekt eindeutig im weitenRaum "mavenisierter" Projekte aufzufinden. Maven-Repositories (öffentliche,private und lokale) sind nach diesen Identifikatoren organisiert. Sobald ein Projektim lokalen Maven-Repository eingestellt ist, steht es augenblicklich für alleweiteren Projekte (welche gegen dieses Repository arbeiten) bereit, eingebundenzu werden. Alles was zu tun bleibt, ist die Koordinaten in einem anderen Projektals Abhängigkeit einzutragen.

Figure 3.5. Der 'Maven'-Raum stellt ein Koordinatensystem für Projekte

3.5.4. Maven RepositoriesBeim ersten Ausführen von Maven, werden Sie feststellen, dass Maven eine Reihevon Dateien von einem Maven Remote Repository herunterlädt. Sollte die

Ein einfaches Maven Projekt

49

Page 69: Maven Definitive Guide De

Erstellung des einführenden Projektes das erste Mal sein, dass Sie Maven aufrufen,so ist das erste was passiert, dass Maven die neuste Version des Resource Pluginsherunterlädt, ausgelöst durch das Goal resources:resource. Maven lädt Artefakteund Plugins aus einem Remote Repository sobald diese benötigt werden. Einer derGründe, weshalb der Maven Download ist so klein ist (1,5 MiB) ist, dass Mavennicht mit allerlei Plugins ausgeliefert wird. Ausgeliefert wird nur das Allernötigste,alles weitere holt sich Maven bei Bedarf aus einem Remote-Repository. Mavenkommt hierzu mit einer Standardeinstellung eines Remote Repositories(http://repo1.maven.org/maven2) von welchem es versuchen wird die CoreMaven-Plugins und deren Abhängigkeiten herunterzuladen.

Oftmals werden Sie an einem Projekt arbeiten, welches auf Bibliotheken aufbaut,welche weder frei noch öffentlich zu verbreiten sind. In einem solchen Fall müssenSie entweder ein benutzerdefiniertes Repository innerhalb Ihrer Organisationaufbauen oder die Abhängigkeiten manuell herunterladen und installieren. DieStandard (Remote) Repositorien können durch Verweise auf benutzerdefinierteMaven Repositorien ersetzt oder ergänzt werden. Es gibt eine Reihe Produktewelche Organisationen dabei unterstützen eigene Repositorien zu verwalten sowieSpiegel-Repositorien von öffentlichen Maven-Repositorien zu erstellen.

Was macht ein Maven Repository zu einem Maven Repository? EinMaven-Repository zeichnet sich durch seine Struktur aus, ein Repository ist eineSammlung von Projekt Artefakten in einer Struktur und Form welche für Mavenleicht leicht nachvollziehbar ist. Die Artefakte in einem Maven-Repository sind ineiner Verzeichnis-Struktur abgelegt, welche eng mit den Maven ProjektKoordinaten korrespondieren. Sie können sich diese Struktur ansehen: öffnen Siemit einem Web-Browser das zentrale Maven Repository aufhttp://repo1.maven.org/maven2/. Sie können dort ein Artefakt mit den Koordinatenorg.apache.commons:commons-email:1.1 im Verzeichnis/org/apache/commons/commons-email/1.1/ in einer Datei namens"commons-email.jar" vorfinden. Der Standard für ein Maven-Repository ist es,einen Artefakten in folgender Struktur relativ zum Wurzelverzeichnis desRepositories abzulegen:/<groupId>/<

artifactId>/<version>/<artifactId>-<version>.<packaging>

Ein einfaches Maven Projekt

50

Page 70: Maven Definitive Guide De

Maven lädt Artefakte und Plugins von einem Remote-Repository auf Ihren lokalenRechner herunter und speichert diese Artefakte in Ihrem lokalenMaven-Repository. Ist ein Artefakt einmal von einem Remote Repository in daslokale Repository heruntergeladen, so wird Maven kein weiteres mal auf dasRemote Repository zugreifen, da Maven immer zuerst auf das lokale Repositoryzugreifen wird, vor es anderswo sucht. Unter Windows XP befindet sich Ihrlokales Repository wahrscheinlich unter C:\Dokumente und

Einstellungen\Benutzername\.m2\Repository, unter Windows Vista befindetsich Ihr lokales Repository unter C:\Benutzer\Benutzername\.m2\Repositoryauf Unix-Systemen werden Sie Ihr lokales Maven-Repository unter~/.m2/repository wiederfinden. Sobald Sie ein Projekt wie das aus demvorherigen Abschnitt erstellen, wird in der Phase install ein Goal ausgeführtwelches den neuen Artifakten in Ihrem lokalen Maven Repository ablegt.

Sie sollten nun in der Lage sein, das Artefakt Ihres eingängigen Projektes in Ihremlokalen Maven Repository wiederzufinden. Beim Aufruf von mvn installinstalliert Maven den Projekt Artefakt in Ihrem lokalen Repository. Probieren Siees aus.

$ mvn install...[INFO] [install:install][INFO] Installing .../simple-1.0-SNAPSHOT.jar to \

~/.m2/repository/com/sonatype/maven/simple/1.0-SNAPSHOT/ \simple-1.0-SNAPSHOT.jar

...

Betrachten Sie die Ausgabe dieses Befehls, Sie können sehen, wie Maven unseresProjekt's JAR-Archiv im lokalen Maven-Repository ablegt. Maven nutzt das lokaleRepository um Artefakte zwischen lokalen Projekten zu teilen. Sagen wir, Sieentwickeln zwei Projekte, Projekt A und Projekt B, dabei ist Projekt B von einemArtefakt von Maven-Projekt A abhängig. So wird Maven den Artefakt beim Buildvon Projekt B aus Ihrem lokalen Repository holen. Maven Repositorien sindbeides, ein lokaler Zwischenspeicher (Cache) von Artefakten welche von einemRemote Repository heruntergeladen wurden, wie auch ein Mechanismus welchererlaubt, dass Ihre Projekte zueinander abhängig sein können.

Ein einfaches Maven Projekt

51

Page 71: Maven Definitive Guide De

3.5.5. Maven Abhängigkeits-Management (DependencyManagement)In dem einfachen Beispiel dieses Kapitels, löst Maven die Koordinaten derJUnit-Abhängigkeit junit:junit:3.8.1 in einen Pfad/junit/junit/3.8.1/junit-3.8.1.jar des Maven Repositories auf. DieMöglichkeit, Artefakte in einem Repository basierend auf Maven Koordinaten zulokalisieren gibt uns die Möglichkeit Abhängigkeiten in einem Projekt POM zudefinieren. Sehen Sie sich die pom.xml-Datei genauer an. Sie werden einenAbschnitt finden, welcher sich mit den Abhängigkeiten (dependencies) befasst,dieser Abschnitt enthält eine einzige Abhängigkeit: JUnit.

Ein komplexeres Projekt würde mehr als eine einzelne Abhängigkeit beinhalten,oder auch Abhängigkeiten welche von anderen Artefakten abhängen. Dies ist eineder mächtigsten Funktionen von Maven: die Unterstützung von transitivenAbhängigkeiten. Nehmen wir an, Ihr Projekt hängt von einer Bibliothek ab, welchewiederum von fünf oder zehn anderen Bibliotheken abhängig ist (etwa wie Springoder Hibernate). Statt alle diese Abhängigkeiten nachzuverfolgen und in Ihrempom.xml explizit aufzulisten können Sie schlicht diejenige Abhängigkeitaufnehmen, welche Sie beabsichtigen. Maven wird die implizit abhängigenBibliotheken für Sie auflösen. Maven wird auch auftretende Konflikte auflösen,diesbezüglich bietet Maven die Möglichkeit das Verhalten anzupassen sowiegewisse transitive Abhängigkeiten auszuschliessen.

Werfen wir einen Blick auf eine Abhängigkeit, welche im vorangegangenenBeispiel in Ihr lokales Repository heruntergeladen wurde. Sehen Sie in Ihremlokalen Repository-Pfad unter ~/.m2/repository/junit/junit/3.8.1/ nach. WieIhnen nach der Lektüre des Kapitels bekannt ist, gibt es eine Dateijunit-3.8.1.jar sowie eine junit-3.8.1.pom-Datei und zusätzlich eine AnzahlDateien; Prüfsummen welche Maven nutzt, um die Echtheit einesheruntergeladenen Artefakts zu überprüfen. Beachten Sie, dass Maven nicht nurdas JUnit JAR-Archive herunterlädt. Maven lädt zusätzlich das POM des Artefaktsherunter. Die Tatsache, dass Maven zusätzlich zum Artefakt auch die zugehörigePOM Dateien herunterlädt ist von zentraler Bedeutung für die Unterstützung vontransitiven Abhängigkeiten durch Maven.

Ein einfaches Maven Projekt

52

Page 72: Maven Definitive Guide De

Sie werden feststellen, dass Maven bei der Installation Ihres Projekt-Artefaktes indas lokale Maven-Repository zugleich eine leicht modifizierte Version derzugehörigen pom.xml-Datei im selben Verzeichnis des JAR-Archives ablegt.Zugleich eine POM-Datei im Repository abzulegen gibt anderen ProjektenInformationen zu diesem Projekt, als Wichtigstes dessen Abhängigkeiten. ProjektB, abhängig von Projekt A, hängt auch von den Abhängigkeiten des Projekts A ab.Wenn Maven eine Abhängigkeit zu einem Artefakt anhand einer Reihe vonArtefaktkoordinaten auflöst, so ruft Maven die POM-Dateien auf und nimmt sichderen Abhängigkeiten an, um bestehende transitive Abhängigkeiten aufzulösen.Jene transitiven Abhängigkeiten werden sodann den Projektabhängigkeitenzugefügt.

Eine Abhängigkeit in Maven ist nicht nur eben ein JAR-Archiv. Es ist einePOM-Datei, welche weitere Abhängigkeiten von anderen Artefakten erklärenkann. Solche Abhängigkeiten der Abhängigkeiten nennt man transitiveAbhängigkeiten und sie werden durch die Tatsache, dass das Maven-Repositorymehr als nur den Bytecode speichert ermöglicht. Maven speichert Meta-Datenbezüglich der Artefakte. Die folgende Abbildung zeigt ein mögliches Szenariotransitiver Abhängigkeiten.

Ein einfaches Maven Projekt

53

Page 73: Maven Definitive Guide De

Figure 3.6. Wie Maven transitive Abhängigkeiten auflöst / Resolvingtransitive Dependencies

In der vorangegangenen Abbildung, ist ein Projekt abhängig von den Projekten Bund C. Projekt B baut auf Projekt D auf, Projekt C baut auf Projekt E auf. DieGesamtmenge aller Abhängigkeiten von A sind Projekte B, C, D, E, jedoch mussteProjekt A lediglich die Projekte B und C deklarieren. Transitive Abhängigkeitenkönnen sich immer dann als nützlich erweisen, wenn Ihr Projekt sich auf einanderes Projekt abstützt, welches weitere Abhängigkeiten deklariert (z.B.Hibernate, Apache Struts, oder das Spring Framework). Maven bietet Ihnen auchdie Möglichkeit, transitive Abhängigkeiten von der Aufnahme in den Klassenpfadauszuschliessen.

Maven kennt auch das Konzept des Gültigkeitsbereichs (Scope). Diepom.xml-Datei des Beispielprojektes kennt nur eine einzige Abhängigkeitjunit:junit:jar:3.8.1 in einem Gültigkeitsbereich test. Wenn eineAbhängigkeit bezüglich eines Gültigkeitsbereich test definiert ist, so bedeutetdies, dass diese nicht dem Goal compile des Compiler Plugins zur Verfügung steht.

Ein einfaches Maven Projekt

54

Page 74: Maven Definitive Guide De

Die Abhängigkeit wird dem Klassenpfad nur für die Goals compiler:testCompilesowie surefire:test zugefügt.

Bei der Erstellung eines JAR-Archivs für ein Projekt werden Abhängigkeiten nichtmit eingeschlossen, es wird lediglich gegen diese kompiliert. Beim Erstellen einesWAR- oder EAR-Archives können Sie konfigurieren, ob Sie Abhängigkeiten inden erstellten Artefakt einschliessen möchten. Ebenfalls möglich ist derAusschluss bestimmter Abhängigkeiten abhängig vom gewähltenGültigkeitsbereich. Der Gültigkeitsbereich provided bestimmt, dass dieAbhängigkeit benötigt wird um zu kompilieren, aber nicht in den ErstelltenArtefakt eingebunden werden soll. Dieser Gültigkeitsbereich (provided) istnützlich, wenn Sie eine Web Applikation erstellen; Sie müssen z.B. gegen dieServlet Spezifikation kompilieren, jedoch werden Sie vermeiden, das Servlet APIJAR-Archiv in das /WEB-INF/lib Verzeichnis Ihrer Webanwendung abzulegen.

3.5.6. Site-Generierung und ReportingEine weitere wichtige Möglichkeit ist die Fähigkeit von Maven Dokumentationenund Reports zu Generieren. Führen Sie im Stammverzeichnis derBeispielanwendung folgenden Aufruf aus:

$ mvn site

Der Aufruf wird Lebenszyklus Phase site auslösen. Im Gegensatz zu denStandard-Build Lifecycle Phasen welche sich mit der Verwaltung vonCodegeneration, der Manipulation von Ressourcen, Kompilierung, Packetierungund so weiter befassen betrifft diese Phase ausschließlich die Bearbeitung vonWebsite-Content im Rahmen des /src/site Verzeichnisses und der Generierungvon Reports. Nach Abschluss diesen Befehls, sollten Sie einen Projekt-Website imVerzeichnis /target/site vorfinden. Nach dem Laden der Datei/target/site/index.html sollten Sie das Skelett eines Projekt (Web-)Sites sehen.Diese Shell enthält bereits einige Berichte unter der Rubrik "Projekt Reports" derlinken Navigation im Menü, sowie Informationen über das Projekt, dessenAbhängigkeiten und der Entwickler im Zusammenhang mit dem Projekt in derRubrik "Projekt-Information". Die Projekt-Website dieses Beispiels ist größtenteils

Ein einfaches Maven Projekt

55

Page 75: Maven Definitive Guide De

leer, da das POM nur sehr wenige Informationen über die reinenKoordinatenpunkte hinaus enthält: den Namen und die URL des Projektsites sowieder einzigen Test Abhängigkeit.

Sie werden feststellen, dass auf dieser Website bereits einige Standard-Berichtebereitstehen, so gibt es einen Bericht, welcher den Ausgang der Unit-Testszusammenfasst. Dieser Report spiegelt alle im Projekt erfolgten Unit Tests wieder,sowie im einzelnen deren Erfolg oder Misserfolg. Ein weiterer Report erzeugt dieJavaDoc des Projekt API's. Maven bietet eine vollständige Palette vonkonfigurierbaren Reports, wie zum Beispiel dem Clover Report, welcherAufschluss über die erfolgte Testabdeckung gibt, der JXR Report, ein Reportwelcher querverweisende HTML-Seiten generiert und speziell für Code-Reviewsnützlich ist, der PMD Report, welcher, aufbauend auf eine Quellcodeanalyse,Aufschluss über eine Reihe von problematischen Kodierungen gibt, sowie derJDepend Report, welcher die Abhängigkeiten zwischen verschiedenen Packageseiner Quellcodebasis analysiert. Die Reports der Website werden in der pom.xmlDatei eines Builds konfiguriert.

3.6. ZusammenfassungWir haben ein einfaches Projekt erstellt, in ein JAR-Archive gepackaged und dieseJAR-Archive im lokalen Repository installiert um es anderen Projekten zurVerfügung zu stellen; sodann haben wir eine Projektwebsite der zugehörigenDokumentation generiert. Wir konnten das alles bewerkstelligen, ohne auch nureinen einzige Zeile Code zu schreiben. Darüberhinaus haben wir Zeit aufgewandteinige der Kernkonzepte von Maven zu erläutern. Im nächsten Kapitel werden wirdamit beginnen, die pom.xml-Datei anzupassen, weitere Abhängigkeiteneinzufügen sowie einige Unit Tests zu konfigurieren.

Ein einfaches Maven Projekt

56

Page 76: Maven Definitive Guide De

Chapter 4. Anpassen eines MavenProjektes

4.1. EinleitungDieses Kapitel baut auf das vorangehende Kapitel (3), insbesondere auf daseingeführte "Simple Maven Projekt" auf. Wir erstellen mit dem Archetype MavenPlugin ein einfaches Projekt, fügen einige Abhängigkeiten ein, erstellen Quellcodeund passen das Projekt unseren Bedürfnissen an. Am Ende dieses Kapitels, werdenSie wissen, wie man es am besten angeht, reale Projekte mit Maven zu erstellen.

4.1.1. Herunterladen der Beispiele dieses KapitelsIn diesem Kapitel werden wir mit Ihnen ein einfaches, nützliches Programm zurInteraktion mit einem Yahoo! Wetter Web-Service erstellen. Während Sie in derLage sein sollten, der Entwicklung dieses Kapitels ohne Beispiel-Quellcode zufolgen, empfehlen wir das Herunterladen einer Kopie der Quellcodes als Referenz.Das Beispielprojekt dieses Kapitel zusammen mit den anderen Beispielen diesesBuchs kann von http://books.sonatype.com/maven-book/mvn-examples-1.0.zipoder http://books.sonatype.com/maven-book/mvn-examples-1.0.tar.gzheruntergeladen werden. Entpacken Sie das Archiv in ein beliebiges Verzeichnis,und gehen Sie dann zum Unterverzeichnis /ch04. Darin befindet sich einVerzeichnis mit dem Namen /simple weather welches den Quellcode für diesesKapitel enthält.

4.2. Eine kurze Einführung in das "SimpleWeather" ProjektBevor wir loslegen, lassen Sie uns einen Schritt zurücktreten und das WetterProjekt vorstellen. Was ist das, das 'Simple Weather' Projekt? Das einfache

57

Page 77: Maven Definitive Guide De

Wetterdienst-Projekt ist ein Beispiel, das speziell konstruiert wurde um einige derFunktionen von Maven vorzustellen. Es handelt sich um eine Anwendung, welchebeispielhaft die Art der Anwendung vorstellt, welche Sie zu bauen angefragtwerden. Die 'Simple Weather' Anwendung ist ein ein grundlegendes Beispiel einerbefehlszeilenorientierten Anwendung, welche auf eine angegebene PostleitzahlDaten vom Yahoo! RSS WetterDienst abruft. Die Anwendung analysiert denRückgabewert und gibt diesen auf der Konsole aus.

Es gab eine Reihe von Gründen welche uns bewogen dieses Beispiel auszuwählen:zum einen ist es unkompliziert, der Benutzer macht eine Eingabe über dieBefehlszeile; diese nehmen wir auf, schicken die Anfrage an den Yahoo! WetterDienst (RSS), analysieren das Ergebnis und geben zuletzt einige Daten auf denBildschirm aus. Dieses Beispiel besteht aus einer einfachen main()-Klasse undeinigen Hilfsklassen, es gibt kein Enterprise Framework einzuführen, lediglicheinfaches XML-Parsen sowie einige Logging-Anweisungen. Zum anderen gibtdieses Beispiel uns eine gute Gelegenheit einige interessante Bibliotheken wieVelocity, Dom4J und Log4J einzuführen - Obwohl dieses Buch sich auf dieEinführung von Maven konzentriert, werden wir uns nicht scheuen, dieGelegenheit zur Einführung interessanter Utilities wahrzunehmen. Schließlich istes ein Beispiel, das in einem einzigen Kapitel eingeführt, entwickelt, und deployedwerden kann.

4.2.1. Yahoo! Wetter Dienst RSSBevor Sie diese Anwendung bauen, sollten Sie etwas mehr über die Yahoo! WetterDienste (RSS-Feeds) erfahren. Allem voraus weisen wir darauf hin, dass derDienst unter den folgenden Konditionen angeboten wird:

"Die Datendienste werden zur Nutzung von Einzelpersonen sowieNon-Profit-Organisationen kostenlos, im Rahmen von persönlichen,nicht kommerziellen Anwendungen angeboten. Sie sind angehaltenauf die Nutzung der Yahoo! Wetter Dienste im Rahmen IhresAngebotes hinzuweisen."

Mit anderen Worten, wenn Sie daran dachten, diese Dienste in Ihren

Anpassen eines Maven Projektes

58

Page 78: Maven Definitive Guide De

kommerziellen Webauftritt zu integrieren, dann gehen Sie bitte noch einmal überdie Bücher: die Nutzung der Dienste ist lediglich für den privaten, nichtkommerziellen Gebrauch gestattet. Die Nutzung zu welcher wir in diesem Kapitelanregen ist beschränkt auf die persönliche Weiterbildung. Weitere Informationenbezüglich der Yahoo! Wetter Dienst Konditionen finden Sie in der Yahoo Wetter!API-Dokumentation: http://developer.yahoo.com/weather/.

4.3. Erstellen des "Simple Weather" ProjektesIn einem ersten Schritt, lassen Sie uns mit Hilfe des Archetype Maven Plugin eingrundlegendes Skelett der Anwendung erstellen. Führen Sie den folgenden Aufrufaus, um ein neues Projekt zu generieren:

$ mvn archetype:create -DgroupId=org.sonatype.mavenbook.ch04 \-DartifactId=simple-weather \-DpackageName=org.sonatype.mavenbook \-Dversion=1.0

[INFO] [archetype:create][INFO] artifact org.apache.maven.archetypes:maven-archetype-quickstart: \

checking for updates from central[INFO] ------------------------------------------------------------------[INFO] Using following parameters for creating Archetype: \

maven-archetype-quickstart:RELEASE[INFO] ------------------------------------------------------------------[INFO] Parameter: groupId, Value: org.sonatype.mavenbook.ch04[INFO] Parameter: packageName, Value: org.sonatype.mavenbook[INFO] Parameter: basedir, Value: ~/examples[INFO] Parameter: package, Value: org.sonatype.mavenbook[INFO] Parameter: version, Value: 1.0[INFO] Parameter: artifactId, Value: simple-weather[INFO] *** End of debug info from resources from generated POM ***[INFO] Archetype created in dir: ~/examples/simple-weather

Sobald das Archetype Maven Plugin das Projekt erstellt hat, wechseln Sie in dasVerzeichnis der "Simple Weather" Anwendung und werfen Sie einen Blick auf diepom.xml Datei. Sie sollten das folgende XML-Dokument vorfinden:

Example 4.1. Erstes POM des "Simple Weather" Projekts

<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

Anpassen eines Maven Projektes

59

Page 79: Maven Definitive Guide De

http://maven.apache.org/maven-v4_0_0.xsd"><modelVersion>4.0.0</modelVersion><groupId>org.sonatype.mavenbook.ch04</groupId><artifactId>simple-weather</artifactId><packaging>jar</packaging><version>1.0</version><name>simple-weather2</name><url>http://maven.apache.org</url><dependencies>

<dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>3.8.1</version><scope>test</scope>

</dependency></dependencies><build>

<plugins><plugin>

<artifactId>maven-compiler-plugin</artifactId><configuration>

<source>1.5</source><target>1.5</target>

</configuration></plugin>

</plugins></build>

</project>

Bitte beachten Sie, dass wir den Parameter version und das Goalarchetype:create übergeben haben. Hiermit wird der Standardwert des1.0-SNAPSHOT überschrieben. In diesem Projekt entwickeln wir die Version 1.0des "Simple Weather" Beispielprogramms, wie Sie leicht am Element version derpom.xml-Datei ablesen können.

4.4. Anpassen der ProjektinformationenBevor wir mit dem Schreiben von Quellcode loslegen, Lassen Sie uns dieProjektinformationen ein wenig Anpassen. Wir wollen etliche Informationenbezüglich der Lizenzrechte, der Organisation sowie der beteiligten Entwicklereinfügen. Dies alles sind Standard-Informationen welche wir erwarten, dass diesein den meisten Projekten gebraucht werden. Die folgende Auflistung zeigt den

Anpassen eines Maven Projektes

60

Page 80: Maven Definitive Guide De

Ausschnitt der XML-Datei, der die Informationen zur Organisation, derLizenzierung sowie bezüglich der Entwickler bereitstellt.

Example 4.2. Informationen bezüglich der Organisation, rechtlicher Belangesowie der Entwickler in der pom.xml-Datei

<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.0http://maven.apache.org/maven-v4_0_0.xsd">

...

<name>simple-weather</name><url>http://www.sonatype.com</url>

<licenses><license><name>Apache 2</name><url>http://www.apache.org/licenses/LICENSE-2.0.txt</url><distribution>repo</distribution><comments>A business-friendly OSS license</comments>

</license></licenses>

<organization><name>Sonatype</name><url>http://www.sonatype.com</url>

</organization>

<developers><developer><id>jason</id><name>Jason Van Zyl</name><email>[email protected]</email><url>http://www.sonatype.com</url><organization>Sonatype</organization><organizationUrl>http://www.sonatype.com</organizationUrl><roles>

<role>developer</role></roles><timezone>-6</timezone>

</developer></developers>

...</project>

Die Auslassungspunkte ("...") in Beispiel 4.2, "Informationen bezüglich derOrganisation, rechtlicher Belange sowie der Entwickler in der pom.xml-Datei"

Anpassen eines Maven Projektes

61

Page 81: Maven Definitive Guide De

sind eine Abkürzung für ein stark gekürztes Listing. Wenn Sie in diesem Buch aufeine pom.xml Datei stossen, welche direkt hinter dem Element-Tag project mit"..." beginnt und mit "..." direkt vor dem Element project aufhört, so zeigt dies an,dass wir nicht die gesamte pom.xml-Datei wiedergeben. In diesem Fall wurden dieTag-Elemente licenses, organization und developers vor dem Elementdependencies eingefügt.

4.5. Einfügen neuer AbhängigkeitenDie einfache "Simpel Weather" Anwendung umfasst folgende drei Aufgaben:Abrufen von XML-Daten vom Yahoo! Wetter Dienst, analysieren deszurückgegebenen XML von Yahoo und schliesslich die formatierte Ausgabe aufder Konsole. Um diese Aufgaben zu erledigen, müssen wir einige neueAbhängigkeiten zu unserem Projekt pom.xml hinzufügen. Um die XML-Antwortdes Yahoo! Dienstes zu analysieren werden wir<term>Dom4J</term>und<term>Jaxen</term>einsetzen. Um die Ausgabe des Kommandozeilenprogramms zu formatieren setzenwir<term>Velocity</term>ein und es ist ebenfalls notwendig, eine Abhängigkeit zu<term>Log4J</term>hinzuzufügen, um Ereignisse zu loggen. Nachdem wir diese Abhängigkeiteneinbringen, sieht unsere Beispiel pom.xml-Datei wie folgt aus:

Example 4.3. Hinzufügen der Abhängigkeiten von Dom4J, Jaxen, Velocityund Log4J

<project>[...]<dependencies>

<dependency><groupId>log4j</groupId><artifactId>log4j</artifactId><version>1.2.14</version>

Anpassen eines Maven Projektes

62

Page 82: Maven Definitive Guide De

</dependency><dependency><groupId>dom4j</groupId><artifactId>dom4j</artifactId><version>1.6.1</version>

</dependency><dependency><groupId>jaxen</groupId><artifactId>jaxen</artifactId><version>1.1.1</version>

</dependency><dependency><groupId>velocity</groupId><artifactId>velocity</artifactId><version>1.5</version>

</dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>3.8.1</version><scope>test</scope>

</dependency></dependencies>[...]

</project>

Wie Sie sehen, haben wir zum bestehenden Element der Test bezogenenAbhängigkeit JUnit, vier weitere Elemente dependency eingefügt. Nach demEinfügen der Abhängigkeiten sowie dem Aufruf von mvn install werden Siesehen, wie Maven diese Abhängigkeiten wie auch eine Reihe transitiverAbhängigkeiten in Ihr lokales Maven Repository herunterlädt.

Nun wie wussten wir die Koordinaten dieser Abhängigkeiten? "Weiss" manschlicht die entsprechenden Werte für groupId und artifactId? MancheArtefakte sind so weit verbreitet (z.B. Log4J), dass Sie, wann immer Sie siebrauchen, sich an die Werte der groupId und artifactId erinnern werden.Velocity, Dom4J und Jaxen haben wir unter Einsatz einer hilfreichen Website(http://www.mvnrepository.com) aufgelöst. Diese Website bietet eine Maske zurSuche über das Maven-Repository an. Sie können die Site benutzen umAbhängigkeiten herauszufinden.

Um dies zu verifizieren, versuchen Sie es selbst: laden Sie die Seitehttp://www.mvnrepository.com und suchen Sie nach einigen häufig verwendeten

Anpassen eines Maven Projektes

63

Page 83: Maven Definitive Guide De

Bibliotheken wie Hibernate oder dem Spring Framework. Wenn Sie auf dieser Sitenach einem Artefakt suchen, werden Ihnen der artefactId-Wert sowie alle imzentralen Repository verfügbaren version-Werte ausgegeben. Ein Klick auf dieDetails einer bestimmten Version lädt eine Seite, welche das Element dependencywiedergibt, dieses können Sie kopieren und in die pom.xml Datei Ihres eigenenProjekt einfügen. Wenn Sie eine Abhängigkeit finden müssen, so lohnt es sich,mvnrepository.com zu benutzen. Auch werden Sie dann werden festestellen, dassbestimmte Bibliotheken über mehr als eine groupId verfügen. Dieses Werkzeugwird Ihnen helfen das Maven-Repository zu verstehen.

4.6. Quellcode von "Simple Weather"Das "Simple Weather" Beispielprogramm besteht aus fünf Java-Klassen:

org.sonatype.mavenbook.weather.Main

Diese Klasse enthält eine statische main()-Funktion, es ist der Ausgangspunktfür dieses System.

org.sonatype.mavenbook.weather.Weather

Die Weather-Klasse ist ein einfaches Java-Bean, welches die Koordinatenunseres Wetterberichtes, sowie eine Anzahl wichtiger Daten wie z.BTemperatur und Luftfeuchtigkeit vorhält.

org.sonatype.mavenbook.weather.YahooRetriever

Die YahooRetriever Klasse stellt eine Verbindung zum Yahoo! Wetter Diensther und liefert einen InputStream der Daten des Dienstes.

org.sonatype.mavenbook.weather.YahooParser

Die YahooParser Klasse analysiert das zurückgegebene XML vom Yahoo!Wetter Dienst und wandelt es in ein Weather-Objekt um.

org.sonatype.mavenbook.weather.WeatherFormatter

Die WeatherFormatter Klasse setzt auf einem Weather Objekt auf, erstellt einenVelocityContext, und führt ein Velocity Template (Vorlage) daruaf aus.

Anpassen eines Maven Projektes

64

Page 84: Maven Definitive Guide De

Wir wollen an diese Stelle nicht näher auf den Quellcode des Beispiels eingehen,werden aber alle erforderlichen Quellen des Beispiels im Buch bereitstellen, sodass Sie "Simple Weather" zum Laufen bekommen. Wir gehen davon aus, dass diemeisten von Ihnen den Quellcode heruntergeladen haben, wollen aber mit denBeispielen des Buches aber auch jene Leser berücksichtigen, welche denBeispielen innerhalb des Kapitels Schritt-für-Schritt folgen. Die folgendenAbschnitte legen den Quellcode der Klassen des "Simple Weather" Projekt dar.Diese Klassen sollten alle in das gleiche Paket com.sonatype.maven.weatherabgelegt werden.

Lassen Sie uns die App sowie die AppTest Klassen welche das Goalarchetype:create erzeugt hat, entfernen und unsere neuen Klassen dem Packagezufügen. In einem Maven-Projekt, liegen alle Quellen eines Projektes unter/src/main/java. Aus dem Basis-Verzeichnis des neuen Projekts heraus, führenSie die folgenden Befehle aus:

$ cd src/test/java/org/sonatype/mavenbook$ rm AppTest.java$ cd ../../../../../..$ cd src/main/java/org/sonatype/mavenbook$ rm App.java$ mkdir weather$ cd weather

Sie haben ein neues Paket com.sonatype.maven.weather erstellt. Nun sollten wiretliche Klassen in diesem Verzeichnis erstellen. Benutzen Sie hierfür IhrenLieblings-Text-Editor und erstellen Sie eine neue Datei mit dem NamenWeather.java mit folgendem Inhalt:

Example 4.4. "Simple Weather" Wetter Objekt Modell

package org.sonatype.mavenbook.weather;

public class Weather {private String city;private String region;private String country;private String condition;private String temp;private String chill;private String humidity;

Anpassen eines Maven Projektes

65

Page 85: Maven Definitive Guide De

public Weather() {}

public String getCity() { return city; }public void setCity(String city) { this.city = city; }

public String getRegion() { return region; }public void setRegion(String region) { this.region = region; }

public String getCountry() { return country; }public void setCountry(String country) { this.country = country; }

public String getCondition() { return condition; }public void setCondition(String condition) { this.condition = condition; }

public String getTemp() { return temp; }public void setTemp(String temp) { this.temp = temp; }

public String getChill() { return chill; }public void setChill(String chill) { this.chill = chill; }

public String getHumidity() { return humidity; }public void setHumidity(String humidity) { this.humidity = humidity; }

}

Die Wetter Klasse definiert ein einfaches Bean, welches die Informationen vorhält,welche aus den vom Yahoo! Wetter-Dienst zurückgegebenen Daten geparsedwerden. Der Yahoo! Wetter-Dienst bietet eine Fülle an Informationen, ausgehendvon den Sonnenaufgangs und -untergangszeiten, bis hin zur Windrichtung sowie-geschwindigkeit. Im Ansinnen, die Beispiele des Buchs so einfach wie möglich zugestalten, enthält unser Weeather-Objekt-Modell nur die Temperatur, Chill,Feuchtigkeit und sowie eine textuelle Beschreibung der gegenwärtigenBedingungen.

Im gleichen Verzeichnis erstellen Sie nun eine Datei mit dem Namen Main.java.Diese Haupt-Klasse wird die statische main()-Funktion beinhalten, welche derEinstiegspunkt dieses Beispiels ist.

Example 4.5. Einfache Weather-Hauptklasse

package org.sonatype.mavenbook.weather;

import java.io.InputStream;

import org.apache.log4j.PropertyConfigurator;

Anpassen eines Maven Projektes

66

Page 86: Maven Definitive Guide De

public class Main {

public static void main(String[] args) throws Exception {// Configure Log4JPropertyConfigurator.configure(Main.class.getClassLoader()

.getResource("log4j.properties"));

// Read the Zip Code from the Command-line (if none supplied, use 60202)String zipcode = "60202";try {zipcode = args[0]);

} catch( Exception e ) {}

// Start the programnew Main(zipcode).start();

}

private String zip;

public Main(String zip) {this.zip = zip;

}

public void start() throws Exception {// Retrieve DataInputStream dataIn = new YahooRetriever().retrieve( zip );

// Parse DataWeather weather = new YahooParser().parse( dataIn );

// Format (Print) DataSystem.out.print( new WeatherFormatter().format( weather ) );

}}

Die oben gezeigte main()-Funktion konfiguriert Log4J indem es eine Ressource(log4j.properties) auf dem Klassenpfad sucht, um anschliessend einenZIP-Code/eine Postleitzahl von der Befehlszeile zu lesen. Wird eine während desLeseversuchs eine Exception geworfen, wird standardmässig auf die Postleitzahl60202 zurückgegriffen. Sobald dem Programm eine Postleitzahl zugeführt wird,wird main() instanziert und deren start()-Methode aufgerufen. Die start()-Methoderuft die YahooRetriever Klasse auf, um das XML-Wetter abzurufen. Die KlasseYahooRetriever liefert einen InputStream, welcher an die Klasse YahooParserweitergeleitet wird. YahooParser analysiert das zurückgegebene Yahoo! WetterXML und erstellt ein Weather-Objekt. Schließlich nimmt der WeatherFormatter

Anpassen eines Maven Projektes

67

Page 87: Maven Definitive Guide De

das Weather Objekt und spuckt eine formatierte Zeichenfolge aus, welche auf derStandardausgabe ausgegeben wird.

Erstellen Sie nun im gleichen Verzeichnis eine Datei mit dem NamenYahooRetriever.java mit folgendem Inhalt:

Example 4.6. "Simple Weather" YahooRetriever Klasse

package org.sonatype.mavenbook.weather;

import java.io.InputStream;import java.net.URL;import java.net.URLConnection;

import org.apache.log4j.Logger;

public class YahooRetriever {

private static Logger log = Logger.getLogger(YahooRetriever.class);

public InputStream retrieve(int zipcode) throws Exception {log.info( "Retrieving Weather Data" );String url = "http://weather.yahooapis.com/forecastrss?p=" + zipcode;URLConnection conn = new URL(url).openConnection();return conn.getInputStream();

}}

Diese einfache Klasse öffnet eine URLConnection auf die Yahoo! Wetter-API undliefert einen InputStream. Um den InputStream verarbeiten zu können müssen wirim gleichen Verzeichnis eine Datei YahooParser.java erstellen.

Example 4.7. "Simple Weather" YahooParser Klasse

package org.sonatype.mavenbook.weather;

import java.io.InputStream;import java.util.HashMap;import java.util.Map;

import org.apache.log4j.Logger;import org.dom4j.Document;import org.dom4j.DocumentFactory;import org.dom4j.io.SAXReader;

public class YahooParser {

Anpassen eines Maven Projektes

68

Page 88: Maven Definitive Guide De

private static Logger log = Logger.getLogger(YahooParser.class);

public Weather parse(InputStream inputStream) throws Exception {Weather weather = new Weather();

log.info( "Creating XML Reader" );SAXReader xmlReader = createXmlReader();Document doc = xmlReader.read( inputStream );

log.info( "Parsing XML Response" );weather.setCity( doc.valueOf("/rss/channel/y:location/@city") );weather.setRegion( doc.valueOf("/rss/channel/y:location/@region") );weather.setCountry( doc.valueOf("/rss/channel/y:location/@country") );weather.setCondition( doc.valueOf("/rss/channel/item/y:condition/@text") );weather.setTemp( doc.valueOf("/rss/channel/item/y:condition/@temp") );weather.setChill( doc.valueOf("/rss/channel/y:wind/@chill") );weather.setHumidity( doc.valueOf("/rss/channel/y:atmosphere/@humidity") );

return weather;}

private SAXReader createXmlReader() {Map<String,String> uris = new HashMap<String,String>();

uris.put( "y", "http://xml.weather.yahoo.com/ns/rss/1.0" );

DocumentFactory factory = new DocumentFactory();factory.setXPathNamespaceURIs( uris );

SAXReader xmlReader = new SAXReader();xmlReader.setDocumentFactory( factory );return xmlReader;

}}

Der YahooParser ist die komplexeste Klasse in diesem Beispiel. Wir werden nichtin die Einzelheiten von Dom4J oder Jaxen eingehen, aber die Klasse benötigtdennoch ein wenig Erklärung. Die parse()-Methode von YahooParser nimmt einenInputStream auf und gibt ein Weather-Objekt zurück. Um dies zu tun, muss sie dasXML-Dokument per DOM4J analysieren. Da wir an Elementen des Yahoo!WetterNamespaces interessiert sind, ist es notwendig einen Namespace bewusstenSAXReader in der Methode createXmlReader() zu erstellen. Sobald wir diesenReader erstellt haben und damit das Dokument analysiert haben, bekommen wirein org.dom4j.Document-Objekt zurück. Statt durch alle Kind-Elemente zuiterieren, adressieren wir die für uns relevanten Informationen direkt mit Hilfe

Anpassen eines Maven Projektes

69

Page 89: Maven Definitive Guide De

eines XPath-Ausdrucks. Dom4J liefert in diesem Beispiel das XML-Parsing undJaxen die XPath-Funktionalität.

Haben wir einmal ein Weather-Objekt erzeugt, müssen wir unsere Ausgabe für denGebrauch lesbar formatieren. Erstellen Sie im Verzeichnis der andern Klassen eineDatei mit dem Namen WeatherFormatter.java.

Example 4.8. "Simple Weather" WeatherFormatter Klasse

package org.sonatype.mavenbook.weather;

import java.io.InputStreamReader;import java.io.Reader;import java.io.StringWriter;

import org.apache.log4j.Logger;import org.apache.velocity.VelocityContext;import org.apache.velocity.app.Velocity;

public class WeatherFormatter {

private static Logger log = Logger.getLogger(WeatherFormatter.class);

public String format( Weather weather ) throws Exception {log.info( "Formatting Weather Data" );Reader reader =new InputStreamReader( getClass().getClassLoader()

.getResourceAsStream("output.vm"));VelocityContext context = new VelocityContext();context.put("weather", weather );StringWriter writer = new StringWriter();Velocity.evaluate(context, writer, "", reader);return writer.toString();

}}

Die Klasse WeatherFormatter verwendet Velocity, um basierend auf einer Vorlage,die Ausgabe darzustellen. Die format()-Methode nimmt eine Weather Bean aufund gibt eine formatierte Zeichenkette zurück. Das erste, was dieformat()-Methode macht, ist eine Velocity Vorlage namens poutput.vm vomKlassenpfad zu laden. Wir erstellen dann einen VelocityContext welcher mit einemeinzigen Weather Objekt namens weather bestückt wird. Ein StringWriter wirderstellt, um die Ergebnisse mit der Vorlage zu fusionieren. Die Vorlage wird mit

Anpassen eines Maven Projektes

70

Page 90: Maven Definitive Guide De

einem Aufruf an Velocity.evaluate() ausgewertet und die Ergebnisse als Stringzurückgegeben.

Bevor wir dieses Beispiel ausführen können, müssen wir noch einige Ressourcenauf unserem Klassenpfad erstellen.

4.7. Resourcen HinzufügenDieses Projekt hängt von zwei Klassenpfad Ressourcen ab: Die main()-Klassekonfiguriert Log4J mit log4j.properties vom Klassenpfad, und dieWeatherFormatter Klasse greift auf eine Velocity Vorlage namens output.vmzurück. Beide Ressourcen müssen im Standard-Package (oder demWurzelverzeichnis des Klassenpfades) vorhanden sein.

Um diese Ressourcen zuzufügen, müssen wir im Wurzelverzeichnis ein neuesVerzeichnis /project-src/main/resources erstellen. Da das Verzeichnis nichtnicht vom Goal archetype:create geschaffen wurde, müssen wir dieses durchfolgende Befehle aus dem Projekt Verzeichnis heraus erstellen:

$ cd src/main$ mkdir resources$ cd resources

Sobald das Ressourcen-Verzeichnis erstellt ist, können wir die beiden Ressourcenzufügen. Zunächst fügen Sie die Datei log4j.properties in dasRessourcen-Verzeichnis ein.

Example 4.9. "Simple Weather" Log4J Konfigurationsdatei

# Set root category priority to INFO and its only appender to CONSOLE.log4j.rootCategory=INFO, CONSOLE

# CONSOLE is set to be a ConsoleAppender using a PatternLayout.log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppenderlog4j.appender.CONSOLE.Threshold=INFOlog4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayoutlog4j.appender.CONSOLE.layout.ConversionPattern=%-4r %-5p %c{1} %x - %m%n

Diese Datei log4j.properties konfiguriert Log4J alle Log-Nachrichten mit

Anpassen eines Maven Projektes

71

Page 91: Maven Definitive Guide De

einem PatternLayout an die Standard-Ausgabe zu senden. Schließlich brauchen wireine Velocity Vorlage output.vm um die Ausgabe der Applikation darzustellen.Erstellen Sie output.vm im Ressourcen-Verzeichnis.

Example 4.10. "Simple Weather" Output-Velocity-Template

*********************************Current Weather Conditions for:${weather.city}, ${weather.region}, ${weather.country}

Temperature: ${weather.temp}Condition: ${weather.condition}Humidity: ${weather.humidity}

Wind Chill: ${weather.chill}*********************************

Diese Vorlage enthält eine Reihe von Verweisen auf eine Variable namensweather. Die Variable weather ist das Weather Bean, welches an denWeatherFormatter gegeben wurde, die Syntax ${weather.temp} ist eine Kurzformum die Werte der Weather Bean aufzurufen und anzuzeigen. Nun, da wir allenProjekt-Code an den richtigen Stellen haben, können wir Maven benutzen, umdieses Beispiel aufzurufen.

4.8. Ausführen des "Simple Weather"ProgrammsMit dem Exec Maven Plugin des Codehouse-Mojo-Projekts können wir dasProgramm ausführen. Um die main()-Klasse auszuführen, geben Sie den folgendenBefehl aus dem Projekt-Basis-Verzeichnis ein:

$ mvn install/$ mvn exec:java -Dexec.mainClass=org.sonatype.mavenbook.weather.Main...[INFO] [exec:java]0 INFO YahooRetriever - Retrieving Weather Data134 INFO YahooParser - Creating XML Reader333 INFO YahooParser - Parsing XML Response420 INFO WeatherFormatter - Formatting Weather Data*********************************Current Weather Conditions for:

Anpassen eines Maven Projektes

72

Page 92: Maven Definitive Guide De

Evanston, IL, US

Temperature: 45Condition: CloudyHumidity: 76

Wind Chill: 38*********************************...

We didn’t supply a command-line argument to the Main class, so we ended up withthe default zip code, 60202. To supply a zip code, we would use the -Dexec.args

argument and pass in a zip code:

$ mvn exec:java -Dexec.mainClass=org.sonatype.mavenbook.weather.Main -Dexec.args="70112"...[INFO] [exec:java]0 INFO YahooRetriever - Retrieving Weather Data134 INFO YahooParser - Creating XML Reader333 INFO YahooParser - Parsing XML Response420 INFO WeatherFormatter - Formatting Weather Data*********************************Current Weather Conditions for:New Orleans, LA, US

Temperature: 82Condition: FairHumidity: 71

Wind Chill: 82*********************************[INFO] Finished at: Sun Aug 31 09:33:34 CDT 2008...

Da wir kein Kommandozeilen-Argument angegeben haben, wurde die Anwendungmit der Standard-Postleitzahl "60202" ausgeführt. Wie Sie sehen können, habenwir das "Simple Weather" Tool erfolgreich aufgerufen, Daten von Yahoo! Wetterabgerufen, das Ergebnis analysiert, formatiert und die sich daraus ergebendenDaten mit Hilfe von Velocity ausgegeben. Wir haben all dies erreicht, ohne dabeiviel mehr zu tun als unseres Projekt's Source-Code erstellen, sowie minimaleKonfiguration an der pom.xml vorzunehmen. Beachten Sie, dass kein"Build-Prozess" beteiligt war. Wir mussten nicht festlegen wie oder wo dieJava-Compiler unseren Quellecode kompiliert, wir haben nichts zu tun, um dasBuild-System zu finden, anzugeben wie der Bytecode ausgeführt werden soll umdie Beispiel-Anwendung auszuführen. Alles, was notwendig war, um etlicheAbhängigkeiten einzufügen war, die entsprechenden Maven Koordinaten zu finden

Anpassen eines Maven Projektes

73

Page 93: Maven Definitive Guide De

und in der pom.xml Datei einzutragen.

4.8.1. Das Exec Maven PluginDer Exec-Plugin ermöglicht Ihnen die Ausführung von Java-Klassen und andereSkripten. Es ist keines der Kern- oder Core-Maven Plugins, aber es steht Ihnenvom Mojo Projekt gehostet von Codehaus zur Verfügung. Für eine vollständigeBeschreibung der Exec-Plugins, führen Sie:

$ mvn help:describe -Dplugin=exec -Dfull

aus

Es wird alle Goals, welche das Exec Maven Plugin zur Verfügung stellt, auflisten.Das Help Plugin, wird auch alle gültigen Parameter des Exec-Plugins aufzeigen,sollten Sie dessen Verhalten anpassen wollen. Um dem Exec PluginKommandozeilenargumente mitzugeben, sehen Sie in der Dokumentation desPlugins nach, welche von help:describe wiedergegeben wird. Obschon dasExec-Plugin nützlich ist, solltens Sie es nicht ausserhalb der laufenden Testswährend der Entwicklungsphase zur Ausführung von Anwendungen benutzen. Alsrobustere Lösung, verwenden Sie das Maven Plugin Assembly welches imAbschnitt 4.13: "Erstellen einer packetierten Kommandozeilen Anwendung"eingeführt wird.

4.8.2. Erkundung der Projekt AbhängigkeitenDas Exec-Plugin ermöglicht es uns, die Anwendung auszuführen, ohne dieAbhängigkeiten explizit in den Klassenpfad aufzunehmen. In jedem anderenBuild-System, wäre es notwendig geworden, alle Abhängigkeiten in eine Art/lib-Verzeichnis zu kopieren, welches eine Sammlung von JAR-Dateien enthält.Dann hätten wir ein einfaches Skript, welches den Bytecode des Programms sowiealle unsere Abhängigkeiten in den Klassenpfad einschliesst. Nur dann könnten wirjava com.sonatype.maven.weather.Main ausführen. Das Exec Plugin nutzt dieTatsache, dass Maven bereits um die Erstellung und Verwaltung IhresKlassenpfades und der Abhängigkeiten weiss.

Anpassen eines Maven Projektes

74

Page 94: Maven Definitive Guide De

Während dies ist zwar bequem ist, ist es dennoch schön zu wissen, was genau inIhren Klassenpfad eingeschlossen wird. Während des Projekt direkt nur vonwenigen Bibliotheken wie Dom4J, Log4J, Jaxen, und Velocity abhängt, stützendiese sich auf weitere, transitive Abhängigkeiten. Um herauszufinden, wie IhrKlassenpfad tatsächlich aussieht, können Sie das Dependency Maven Plugin zurAusgabe einer Liste der aufgelösten Abhängigkeiten benutzen. Um die Liste derAbhängigkeiten des "Simple Weather" Projekts auszugeben, führen Sie das Goaldependency:resolve aus.

$ mvn dependency:resolve...[INFO] [dependency:resolve][INFO][INFO] The following files have been resolved:[INFO] com.ibm.icu:icu4j:jar:2.6.1 (scope = compile)[INFO] commons-collections:commons-collections:jar:3.1 (scope = compile)[INFO] commons-lang:commons-lang:jar:2.1 (scope = compile)[INFO] dom4j:dom4j:jar:1.6.1 (scope = compile)[INFO] jaxen:jaxen:jar:1.1.1 (scope = compile)[INFO] jdom:jdom:jar:1.0 (scope = compile)[INFO] junit:junit:jar:3.8.1 (scope = test)[INFO] log4j:log4j:jar:1.2.14 (scope = compile)[INFO] oro:oro:jar:2.0.8 (scope = compile)[INFO] velocity:velocity:jar:1.5 (scope = compile)[INFO] xalan:xalan:jar:2.6.0 (scope = compile)[INFO] xerces:xercesImpl:jar:2.6.2 (scope = compile)[INFO] xerces:xmlParserAPIs:jar:2.6.2 (scope = compile)[INFO] xml-apis:xml-apis:jar:1.0.b2 (scope = compile)[INFO] xom:xom:jar:1.0 (scope = compile)

Wie Sie sehen, hat unser Projekt eine große Zahl von Abhängigkeiten. Auch wennwir nur direkte Abhängigkeiten von vier Bibliotheken angegeben haben, scheineninsgesamt 15 Abhängigkeiten zu bestehen. Dom4J hängt von Xerces und demXML-Parser-APIs ab, Jaxen hängt davon ab, dass Xalan sich im Klassenpfadbefindet. Das Dependency Maven Plugin wird ihnen die endgültige Kombinationvon Abhängigkeiten unter welchen Ihr Projekt kompiliert wird ausgeben. WollenSie den gesamten Baum aller Abhängigkeiten sehen, so können Sie das Goaldependency:tree aufrufen:

$ mvn dependency:tree...[INFO] [dependency:tree][INFO] org.sonatype.mavenbook.ch04:simple-weather:jar:1.0[INFO] +- log4j:log4j:jar:1.2.14:compile

Anpassen eines Maven Projektes

75

Page 95: Maven Definitive Guide De

[INFO] +- dom4j:dom4j:jar:1.6.1:compile[INFO] | \- xml-apis:xml-apis:jar:1.0.b2:compile[INFO] +- jaxen:jaxen:jar:1.1.1:compile[INFO] | +- jdom:jdom:jar:1.0:compile[INFO] | +- xerces:xercesImpl:jar:2.6.2:compile[INFO] | \- xom:xom:jar:1.0:compile[INFO] | +- xerces:xmlParserAPIs:jar:2.6.2:compile[INFO] | +- xalan:xalan:jar:2.6.0:compile[INFO] | \- com.ibm.icu:icu4j:jar:2.6.1:compile[INFO] +- velocity:velocity:jar:1.5:compile[INFO] | +- commons-collections:commons-collections:jar:3.1:compile[INFO] | +- commons-lang:commons-lang:jar:2.1:compile[INFO] | \- oro:oro:jar:2.0.8:compile[INFO] +- org.apache.commons:commons-io:jar:1.3.2:test[INFO] \- junit:junit:jar:3.8.1:test...

Sollten Sie wirklich abenteuerlich sein, oder einfach nur die volle Liste derAbhängigkeiten sehen wollen, einschliesslich aller Artefakte, die aufgrund vonKonflikten oder anderen Gründen abgelehnt wurden, führen Sie Maven mit demDebug-Flag aus.

$ mvn install -X...[DEBUG] org.sonatype.mavenbook.ch04:simple-weather:jar:1.0 (selected for null)[DEBUG] log4j:log4j:jar:1.2.14:compile (selected for compile)[DEBUG] dom4j:dom4j:jar:1.6.1:compile (selected for compile)[DEBUG] xml-apis:xml-apis:jar:1.0.b2:compile (selected for compile)[DEBUG] jaxen:jaxen:jar:1.1.1:compile (selected for compile)[DEBUG] jaxen:jaxen:jar:1.1-beta-6:compile (removed - )[DEBUG] jaxen:jaxen:jar:1.0-FCS:compile (removed - )[DEBUG] jdom:jdom:jar:1.0:compile (selected for compile)[DEBUG] xml-apis:xml-apis:jar:1.3.02:compile (removed - nearer: 1.0.b2)[DEBUG] xerces:xercesImpl:jar:2.6.2:compile (selected for compile)[DEBUG] xom:xom:jar:1.0:compile (selected for compile)[DEBUG] xerces:xmlParserAPIs:jar:2.6.2:compile (selected for compile)[DEBUG] xalan:xalan:jar:2.6.0:compile (selected for compile)[DEBUG] xml-apis:xml-apis:1.0.b2.[DEBUG] com.ibm.icu:icu4j:jar:2.6.1:compile (selected for compile)[DEBUG] velocity:velocity:jar:1.5:compile (selected for compile)[DEBUG] commons-collections:commons-collections:jar:3.1:compile (selected for compile)[DEBUG] commons-lang:commons-lang:jar:2.1:compile (selected for compile)[DEBUG] oro:oro:jar:2.0.8:compile (selected for compile)[DEBUG] junit:junit:jar:3.8.1:test (selected for test)

In der Debug-Ausgabe sehen wir die Eingeweide desAbhängigkeit-Management-Systems bei der Arbeit. Was Sie hier sehen, ist dervollständige Baum aller Abhängigkeiten für dieses Projekt. Maven listet die vollen

Anpassen eines Maven Projektes

76

Page 96: Maven Definitive Guide De

Maven Koordinaten für alle Ihre Projekt-Abhängigkeiten und die Abhängigkeitenderer Abhängigkeiten (und die Abhängigkeiten der Abhängigkeiten derAbhängigkeiten). Sie können ersehen, dass "Simple Weather" von Jaxen abhängt,welches von xom abhängig ist was wiederum auf icu4j aufbaut. Aus dieserAusgabe können Sie erkennen, dass Maven einen Graphen der Abhängigkeitenerstellt, hierbei Duplikate eliminiert und etwaige Versionskonflikte löst. Sollten Siebezüglich Abhängigkeiten auf Probleme stossen, so ist es oft hilfreich ein wenigtiefer im Baum den Abhängigkeiten zu schürfen. Durch Einschalten derDebugausgabe können Sie den Maven Abhängigkeits-Mechanismus bei der Arbeitsehen.

4.9. Erstellen von Unit-TestsMaven hat eine eingebaute Unterstützung für Unit-Tests. Testen ist ein Teil desursprünglichen Maven Lebenszyklus. Lassen Sie uns ein paar Unit-Tests zum"Simple Weather" Projekt hinzufügen. Zuerst werden wir ein Packagecom.sonatype.maven.weather unter /src/test/java erstellen.

$ cd src/test/java$ cd org/sonatype/mavenbook$ mkdir -p weather/yahoo$ cd weather/yahoo

An dieser Stelle werden wir zwei Unit-Tests erstellen. Der erste Unit Test wird dieKlasse YahooParser, der zweite die WeatherFormatter Klasse testen. Erstellen Sieeine Datei mit dem Namen YahooParserTest.java mit folgendem Inhalt im Packageweather:

Example 4.11. "Simple Weather" YahooParserTest Unit-Test

package org.sonatype.mavenbook.weather.yahoo;

import java.io.InputStream;

import junit.framework.TestCase;

import org.sonatype.mavenbook.weather.Weather;import org.sonatype.mavenbook.weather.YahooParser;

Anpassen eines Maven Projektes

77

Page 97: Maven Definitive Guide De

public class YahooParserTest extends TestCase {

public YahooParserTest(String name) {super(name);

}

public void testParser() throws Exception {InputStream nyData =getClass().getClassLoader().getResourceAsStream("ny-weather.xml");

Weather weather = new YahooParser().parse( nyData );assertEquals( "New York", weather.getCity() );assertEquals( "NY", weather.getRegion() );assertEquals( "US", weather.getCountry() );assertEquals( "39", weather.getTemp() );assertEquals( "Fair", weather.getCondition() );assertEquals( "39", weather.getChill() );assertEquals( "67", weather.getHumidity() );

}}

Dieser YahooParserTest erweitert die von JUnit definierte Klasse TestCase. Esfolgt das übliche Muster für einen JUnit-Test: ein Konstruktor, der ein einzigesString Argument annimmt und den Konstruktor der Superklasse aufruft und eineReihe von public Methoden welche im Namen mit "test" beginnen, welche alsUnit-Tests aufgerufen werden. Wir definieren ein einfaches Testvorgehen:testParser, welches den YahooParser überprüft, indem es ein XML-Dokument mitbekannten Werten auswertet. Das Test XML-Dokument heisst ny-weather.xml undwird aus dem Klassenpfad geladen. Wir fügen Test Ressourcen in Abschnitt 4.11:"Hinzufügen von Unit Test Ressourcen" an. In unserem Verzeichnisaufbau desMaven-Projekts befindet sich die Datei ny-weather.xml im Verzeichnis derTest-Ressourcen -- ${basedir}/src/test/resources unter/com/sonatype/maven/wetter/yahoo/ny-weather.xml . Die Datei wird alsInputStream eingelesen und an die parse()-Methode des YahooParser geleitet. Dieparse()-Methode liefert ein Weather Objekt zurück,welches über eine Anzahl vonassertEquals() Aufrufen, eine innerhalb der Klasse TestCase definierte Methode,geprüft wird.

Im gleichen Verzeichnis erstellen Sie eine Datei mit dem NamenWeatherFormatterTest.java.

Anpassen eines Maven Projektes

78

Page 98: Maven Definitive Guide De

Example 4.12. "Simple Weather" WeatherFormatterTest Unit-Test

package org.sonatype.mavenbook.weather.yahoo;

import java.io.InputStream;

import org.apache.commons.io.IOUtils;

import org.sonatype.mavenbook.weather.Weather;import org.sonatype.mavenbook.weather.WeatherFormatter;import org.sonatype.mavenbook.weather.YahooParser;

import junit.framework.TestCase;

public class WeatherFormatterTest extends TestCase {

public WeatherFormatterTest(String name) {super(name);

}

public void testFormat() throws Exception {InputStream nyData =getClass().getClassLoader().getResourceAsStream("ny-weather.xml");

Weather weather = new YahooParser().parse( nyData );String formattedResult = new WeatherFormatter().format( weather );InputStream expected =getClass().getClassLoader().getResourceAsStream("format-expected.dat");

assertEquals( IOUtils.toString( expected ).trim(),formattedResult.trim() );

}}

Der zweite Unit Test dieses einfachen Projekts testet die Klasse WeatherFormatter.Wie bereits die Klasse YahooParserTest, erweitert auch die KlasseWeatherFormatterTest die Klasse TestCase des JUnit Frameworks. Die einzigeTestfunktion liest die gleiche Testressource von ${basedir}/src/test/resources

umter dem /com/sonatype/Maven/Wetter/yahoo-Verzeichnis unter Einbezug desUnit Test Klassenpfades. Wir fügen Test Ressourcen wie im Abschnitt 4.11:"Hinzufügen Unit Test Ressourcen" beschrieben an. WeatherFormatterTest ziehtdiese Beispiel-Eingabedatei durch den YahooParser an, welcher ein WeatherObjekt zurückgibt an, dieses Objekt wird dann mit dem WeatherFormatterausgegeben. Da WeatherFormatter eine Zeichenkette ausgibt, müssen wir gegeneine gesetzte Eingabe testen. Unser "gesetzter" Input wurde einer Text-Datei mit

Anpassen eines Maven Projektes

79

Page 99: Maven Definitive Guide De

dem Namen format-expected.dat erfasst, welche sich im gleichen Verzeichniswie ny-weather.xml befindet. Um die Test-Ausgabe mit der erwarteten Ausgabe zuvergleichen, wird diese als InputStream eingelesen und unter Einbezug der KlasseIOUtils aus Apache Commons IO in einen String umgewandelt. DieseZeichenfolge wird dann mittels assertEquals() mit der Testausgabe verglichen.

4.10. Hinzufügen von Gebietsbezogenen UnitTestsIn der Klasse WeatherFormatterTest benutzten wir ein Dienstprogramm ausApache Commons IO- die Klasse IOUtils. IOUtils bietet eine Reihe vonhilfreichen statische Funktionen, welche den Grossteil der Arbeit derInput/Output-Operationen übernehmen. In diesem speziellen Unit Test benutzenwir IOUtils.toString() um die format-expected.dat Klassenpfadressource ineinen String zu verwandeln. Wir hätten dies auch ohne den Einsatz von CommonsIO bewerkstelligen können, aber es hätte zusätzliche sechs oder sieben ZeilenCode bedingt um mit den verschiedenen InputStreamReader- undStringWriter-Objekten umzugehen. Der Hauptgrund für den Einsatz der CommonsIO Bibliothek war, uns eine Ausrede zu geben, Test-scoped (abgegrenzte)Abhängigkeiten am Beispiel von Commons IO einzuführen.

Eine Test-scoped Abhängigkeit ist eine Abhängigkeit, welche nur auf während derTestkompilierung und Testausführung auf dem Klassenpfad eingeschlossen ist.Sollte Ihr Projekt als war- oder ear-Archiv packetiert werden, so würde eineTest-scoped Abhängigkeit nicht in das Projekt-Output-Archiv einbezogen werden.Um eine Test-scoped Abhängigkeit zu erstellen, fügen Sie das folgendedependency-Element in Ihrem Projekt in das Element dependencies ein .

Example 4.13. Das Hinzufügen einer Test-scoped Abhängigkeit

<project>...<dependencies>

...<dependency><groupId>org.apache.commons</groupId>

Anpassen eines Maven Projektes

80

Page 100: Maven Definitive Guide De

<artifactId>commons-io</artifactId><version>1.3.2</version><scope>test</scope>

</dependency>...

</dependencies></project>

Nachdem Sie diese Abhängigkeit der pom.xml Datei zugefügt haben, führen Siemvn dependency:resolve aus: Sie sollten sehen, dass commons-io nun als eineAbhängigkeit mit Gebietsbezug test aufgeführt wird. Noch eine Kleinigkeit wirdbenötigt, bevor wir bereit sind diese Projekt Unit-Tests auszuführen. Wir müssendie Klassenpfad Ressourcen erstellen, von welchen diese Unit-Tests abhängen.Geltungsbereiche werden ausführlich im Abschnitt 9.4.1: "Geltungsbereiche vonAbhängigkeiten" dargestellt.

4.11. Hinzufügen einer Unit-Test RessourceEin Unit Test hat Zugriff auf eine Reihe von testspezifischen Ressourcen. Häufigwerden Sie Dateien mit den erwarteten Ergebnissen und Dummy-Dateien mitEingabetexten auf dem Testklassenpfad ablegen. In diesem Projekt legen wir einTest XML-Dokument für den YahooParserTest namens ny-weather.xml sowieeine Datei mit der erwarteten Ausgabe des WeatherFormatter unterformat-expected.dat ab.

Um Test Ressourcen einzufügen, müssen Sie zunächst das Verzeichnis/src/test/resources erstellen. Dies ist das Standard-Verzeichnis, in welchemMaven nach Unit-Test Ressourcen sucht. Erstellen Sie dieses Verzeichnis indemSie die folgenden Befehle aus Ihrem Projekt-Basis-Verzeichnis heraus ausführen.

$ cd src/test$ mkdir resources$ cd resources

Sobald Sie das Ressourcen-Verzeichnis erstellt haben, erstellen Sie eine Datei mitdem Namen format-expected.dat in diesem Verzeichnis.

Anpassen eines Maven Projektes

81

Page 101: Maven Definitive Guide De

Example 4.14. "Simple Weather" WeatherFormatterTest erwartete Ausgabe

*********************************Current Weather Conditions for:New York, NY, US

Temperature: 39Condition: FairHumidity: 67

Wind Chill: 39*********************************

Diese Datei sollte vertraut aussehen. Es ist die gleiche Ausgabe, welche generiertwurde, als Sie das "Simple Weather" Projekt mit dem Exec Maven Exec starteten.Die zweite Datei welche Sie benötigen werden und daher imRessourcen-Verzeichnis erstellen sollten ist ny-weather.xml.

Example 4.15. "Simple Weather" YahooParserTest XML-Eingabe

<?xml version="1.0" encoding="UTF-8" standalone="yes" ?><rss version="2.0" xmlns:yweather="http://xml.weather.yahoo.com/ns/rss/1.0"

xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#"><channel><title>Yahoo! Weather - New York, NY</title><link>http://us.rd.yahoo.com/dailynews/rss/weather/New_York__NY/</link><description>Yahoo! Weather for New York, NY</description><language>en-us</language><lastBuildDate>Sat, 10 Nov 2007 8:51 pm EDT</lastBuildDate>

<ttl>60</ttl><yweather:location city="New York" region="NY" country="US" /><yweather:units temperature="F" distance="mi" pressure="in" speed="mph" /><yweather:wind chill="39" direction="0" speed="0" /><yweather:atmosphere humidity="67" visibility="1609" pressure="30.18"

rising="1" /><yweather:astronomy sunrise="6:36 am" sunset="4:43 pm" /><image>

<title>Yahoo! Weather</title>

<width>142</width><height>18</height><link>http://weather.yahoo.com/</link><url>http://l.yimg.com/us.yimg.com/i/us/nws/th/main_142b.gif</url></image><item><title>Conditions for New York, NY at 8:51 pm EDT</title>

Anpassen eines Maven Projektes

82

Page 102: Maven Definitive Guide De

<geo:lat>40.67</geo:lat><geo:long>-73.94</geo:long><link>http://us.rd.yahoo.com/dailynews/rss/weather/New_York__NY/\</link>

<pubDate>Sat, 10 Nov 2007 8:51 pm EDT</pubDate><yweather:condition text="Fair" code="33" temp="39"

date="Sat, 10 Nov 2007 8:51 pm EDT" /><description><![CDATA[<img src="http://l.yimg.com/us.yimg.com/i/us/we/52/33.gif" /><br /><b>Current Conditions:</b><br />Fair, 39 F<BR /><BR /><b>Forecast:</b><BR />Sat - Partly Cloudy. High: 45 Low: 32<br />Sun - Sunny. High: 50 Low: 38<br />

<br />]]></description><yweather:forecast day="Sat" date="10 Nov 2007" low="32" high="45"

text="Partly Cloudy" code="29" />

<yweather:forecast day="Sun" date="11 Nov 2007" low="38" high="50"text="Sunny" code="32" />

<guid isPermaLink="false">10002_2007_11_10_20_51_EDT</guid></item></channel></rss>

Diese Datei enthält ein Test XML-Dokument für den YahooParserTest. Wirspeichern diese Datei, um den YahooParser zu testen ohne eine XML-Antwortvom Yahoo! Wetterdienst abzurufen.

4.12. Ausführen von Unit-TestsNun, da die Unit Tests zu Ihrem Projekt bestehen, führen wir diese aus! Siebrauchen nichts weiter zu tun um die Unit Tests anzustossen: die Testphase ist einnormaler Bestandteil des Maven Lifecycles. Die Maven Tests werden automatischausgeführt sobald Sie mvn package oder mvn install aufrufen. Wenn Siemöchten, dass alle Phasen des Lebenszyklus bis einschließlich der Testphaseausgeführt werden, rufen Sie mvn test auf.

$ mvn test...[INFO] [surefire:test][INFO] Surefire report directory: ~/examples/simple-weather/target/\

surefire-reports

Anpassen eines Maven Projektes

83

Page 103: Maven Definitive Guide De

-------------------------------------------------------T E S T S-------------------------------------------------------Running org.sonatype.mavenbook.weather.yahoo.WeatherFormatterTest0 INFO YahooParser - Creating XML Reader177 INFO YahooParser - Parsing XML Response239 INFO WeatherFormatter - Formatting Weather DataTests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.547 secRunning org.sonatype.mavenbook.weather.yahoo.YahooParserTest475 INFO YahooParser - Creating XML Reader483 INFO YahooParser - Parsing XML ResponseTests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.018 sec

Results :

Tests run: 2, Failures: 0, Errors: 0, Skipped: 0

Der Aufruf von mvn test von der Befehlszeile veranlasst Maven zur Ausführungaller Phasen des Lebenszyklus' bis und mit der Testphase. Das Surefire MavenPlugin hat ein Goal test, welches an die Testphase gebunden ist. Dieses Goal testführt alle Unit-Tests des Projektes aus, welche sich unter /src/test/javabefinden und einen Dateinamen von **/Test*.java, **/*Test.java sowie**/*TestCase.java haben. Für unser Projekt können Sie sehen, dass das SurefirePlugin mit dem Goal test die Unittests WeatherFormatterTest undYahooParserTest ausgeführt hat. Bei der Ausführung des Surefire Maven Pluginswerden nicht nur die Tests ausgeführt, es werden auch XML- und Text-Berichteerzeugt und unter dem Verzeichnis ${basedir}/target/surefire-reportsabgelegt. Sollten Ihre Tests fehlschlagen, so können Sie in diesem Verzeichnis vonden Unit Tests angelegte Detailsangaben wie Fehlermeldungen oder Stack Tracesder Unit Tests finden.

4.12.1. Ignorieren fehlgeschlagener Unit TestsOft werden Sie an einem System arbeiten dessen Unit Tests scheitern. Sollten SieTest-Driven Development (TDD) praktizieren, so ist die Anzahl der gescheitertenTests ein Mass dafür, wie nahe der Vollendung Ihr Projekt ist. Sollten Sie trotzgescheiterter Unit-Tests dennoch gerne ein Produkt erstellen, so müssen Sie Mavenanweisen, diese Unit Tests zu ignorieren. Das Standardverhalten von Maven ist es,nach einem gescheiterten Test den Build abzubrechen. Um den Build auch bei

Anpassen eines Maven Projektes

84

Page 104: Maven Definitive Guide De

auftreten gescheiterter Tests weiterzuführen, müssen sie den KonfigurationseintragtestFailureIgnore des Surefire Maven Plugins auf "true" setzen.

Example 4.16. Ignorieren von gescheiterten Unit-Test Fällen

<project>[...]<build>

<plugins><plugin>

<groupId>org.apache.maven.plugins</groupId><artifactId>maven-surefire-plugin</artifactId><configuration>

<testFailureIgnore>true</testFailureIgnore></configuration>

</plugin></plugins>

</build>[...]

</project>

Die Plugin Dokumentation(http://maven.apache.org/plugins/maven-surefire-plugin/test-mojo.html) zeigt, dassdieser Parameter einen Ausdruck erklärt:

Example 4.17. Plugin-Parameter-Ausdrücke

testFailureIgnore Set this to true to ignore a failure during \testing. Its use is NOT RECOMMENDED, but quite \convenient on occasion.

* Type: boolean* Required: No* Expression: ${maven.test.failure.ignore}

// testFailureIgnore Setzen Sie diesen Parameter auf "true" um scheiternde Testfälle zu ignorieren.// Die Benutzung des Parameters wird NICHT EMPFOHLEN, kann aber zu Zeiten recht nützlich sein!

Dieser Ausdruck kann von der Befehlszeile mit dem 'D-Parameter' gesetzt werden:

$ mvn test -Dmaven.test.failure.ignore=true

Anpassen eines Maven Projektes

85

Page 105: Maven Definitive Guide De

4.12.2. Überspringen von Unit-TestsSie können Maven auch so konfigurieren dass bestimmte Unit Tests ganzübersprungen werden. Vielleicht arbeiten Sie an einem sehr großen System, inwelchem die Unit-Tests Minuten in Anspruch nehmen und Sie wollen nicht wartenbis alle Unit-Tests abgeschlossen sind, um ein Produkt zu erstellen.Möglicherweise müssen Sie mit einem Legacy-System arbeiten, welches eineReihe von scheiternden Unit-Tests umfasst. Anstelle der Behebung der Unit-TestFehlern möchten Sie lediglich ein JAR-Archive erstellen. Maven sieht für dieseFälle vor, und stellt die Fähigkeit mit dem Parameter skip des Surefire Pluginszum überspringen von Unit-Tests bereit. Um beim Aufruf von der Befehlszeile dieUnit Tests zu überspringen, fügen Sie einfach den Parameter maven.test.skipIhrem angestrebten Goal an:

$ mvn install -Dmaven.test.skip=true...[INFO] [compiler:testCompile][INFO] Not compiling test sources[INFO] [surefire:test][INFO] Tests are skipped....

Wenn das Surefire Plugin das Goal test erreicht, überspringt es die Unit-Tests,sollte der Parameter maven.test.skip auf "true" gesetzt sein. Eine weitere ArtMaven zu konfigurieren Unit Tests zu überspringen ist, diese Konfiguration inIhrer Projekt pom.xml-Datei vorzunehmen. Um dies zu tun, würde man einElement plugin Ihrer Build Konfiguration zufügen.

Example 4.18. Überspringen von Unit-Tests

<project>[...]<build>

<plugins><plugin>

<groupId>org.apache.maven.plugins</groupId><artifactId>maven-surefire-plugin</artifactId><configuration>

<skip>true</skip></configuration>

</plugin></plugins>

Anpassen eines Maven Projektes

86

Page 106: Maven Definitive Guide De

</build>[...]

</project>

4.13. Builden einer paketierten, Befehlszeilenorientierten AnwendungIn Abschnitt 4.8, "Ausführen des "Simple Weather" Programms", haben wir die"Simple Weather" Anwendung unter Verwendung des Exec Maven Pluginsausgeführt. Auch wenn das Exec Plugin die Anwedung ausgeführt und undAusgaben erzeugt hat, sollte man Maven nicht als Container zur Ausführung IhrerAnwendungen ansehen. Sollten Sie Ihre Anwendung an andere weitergebenwollen, so werden sie dies sicher als JAR-Archive, oder als Archiv in ZIP oderGZIP Format tun wollen. Das folgende Vorgehen gibt einen Überblick über denProzess mit Hilfe einer vordefinierter Assembly Definitionsdatei sowie demAssembly Maven Plugin ein verteilbares JAR Archive zu erstellen, welches denAnwendungs-(Byte) Code sowie alle notwendigen Abhängigkeiten enthält.

Das Assembly Maven Plugin ist ein Plugin welches Sie zur Zusammenstellungbeliebiger Pakete zur Verteilung Ihrer Anwendung benutzen können. Sie könnendas Assembly Plugin dazu einsetzen, das Resultat in beliebiger Formzusammenzustellen, indem Sie eine darauf zugeschnittene AssemblyDefinitionsdatei erstellen. In einem späteren Kapitel werden wir Ihnen zeigen wieman eine zugeschnittene Assembly Definitionsdatei erstellt, um die "SimpleWeather" in einem komplexeren Archiv zu deployen. In diesem Kapitel werdenwir das vordefinierte Format jar-with-dependencies einsetzen. Um dasAssembly Maven Plugin einzusetzen, müssen wir die folgende Anpassung an derbestehenden Konfiguration in der pom.xml-Datei vornehmen:

Example 4.19. Konfigurieren der Assembly Maven Definition

<project>[...]<build>

<plugins>

Anpassen eines Maven Projektes

87

Page 107: Maven Definitive Guide De

<plugin><artifactId>maven-assembly-plugin</artifactId><configuration>

<descriptorRefs><descriptorRef>jar-with-dependencies</descriptorRef>

</descriptorRefs></configuration>

</plugin></plugins>

</build>[...]

</project>

Sobald Sie die Konfigurationsänderung hinzugefügt haben, starten Sie den Buildmit dem Aufruf mvn assembly:assembly.

$ mvn install assembly:assembly...[INFO] [jar:jar][INFO] Building jar: ~/examples/simple-weather/target/simple-weather-1.0.jar[INFO] [assembly:assembly][INFO] Processing DependencySet (output=)[INFO] Expanding: \

.m2/repository/dom4j/dom4j/1.6.1/dom4j-1.6.1.jar into \/tmp/archived-file-set.1437961776.tmp

[INFO] Expanding: .m2/repository/commons-lang/commons-lang/2.1/\commons-lang-2.1.jar

into /tmp/archived-file-set.305257225.tmp... (Maven Expands all dependencies into a temporary directory) ...[INFO] Building jar: \

~/examples/simple-weather/target/\simple-weather-1.0-jar-with-dependencies.jar

Nach dem Abarbeiten befindet sich unser Ergebnis im Verzeichnis /target in derDatei simple-weather-1.0-jar-with-dependencies.jar. Wieder können wir diemain()-Klasse ausführen. Um die "Simple Weather" aufzurufen, geben siefolgenden Befehl auf der Kommandozeile im Projekt-Basis-Verzeichnis ein:

$ cd target$ java -cp simple-weather-1.0-jar-with-dependencies.jar \

org.sonatype.mavenbook.weather.Main 100020 INFO YahooRetriever - Retrieving Weather Data221 INFO YahooParser - Creating XML Reader399 INFO YahooParser - Parsing XML Response474 INFO WeatherFormatter - Formatting Weather Data*********************************Current Weather Conditions for:New York, NY, US

Anpassen eines Maven Projektes

88

Page 108: Maven Definitive Guide De

Temperature: 44Condition: FairHumidity: 40

Wind Chill: 40*********************************

Das Format 'jar-with-dependencies' erstellt ein einziges JAR-Archiv, welches allenBytecode der "Simple Weather" Anwendung sowie den entpackten Bytecode allerAbhängigkeiten enthält. Dieses etwas unkonventionelle Format eines Archivskommt in einer 9 MiB Datei mit rund 5290 Klassen daher, aber dies ist eineinfaches Format, um Anwendungen welche Sie mit Maven entwickelt haben zuverteilen. Später in diesem Buch, werden wir Ihnen zeigen, wie Sie einebenutzerdefinierte Assembly Definitionsdatei erstellen um eine gewöhnlicheStandard-Distribution zu erstellen.

4.13.1. Anbinden des Assembly Goals zurPacketierungs PhaseUnter Maven 1 wurde ein Build massgeschneidert indem man eine Anzahl vonPlugin Goals direkt aneinanderknüpfte. Jedes Plugin Goal hatte Vorbedingungenund definierte einen Bezug zu anderen Plugin Goals. Mit der Ankunft von Maven2 wurde das Lebenszykluskonzept eingeführt, und Plugin Goals sind nun zu einerAnzahl Phasen zugeordnet, welche in einem Standard Lebenszyklus bestehen. DasLebenszykluskonzept ist eine solide Grundlage, welche es vereinfacht, die Abfolgeder Plugin Goals zu bestimmen und zu koordinieren welche Goals in einembestimmten Build ausgeführt werden. Unter Maven 1 war der Bezug zwischen denGoals direkt, unter Maven 2 besteht der Bezug von Plugin Goals immer zu einerMenge von gemeinsamen Lebenszyklus Phasen. Obschon es zulässig ist, ein Goaldirekt - wie beschrieben - von der Kommandozeile auszuführen, steht es eher imEinklang mit der Architektur von Maven 2, das Assembly mit dem Aufruf desGoals assembly:assembly während einer Phase des Maven Lebenszyklusesaufzurufen.

Die folgenden Plugin-Konfiguration konfiguriert das Maven Plugin Assembly, umdas angefügte Goal während der Phase package des Maven Standard

Anpassen eines Maven Projektes

89

Page 109: Maven Definitive Guide De

Lebenszykluses auszuführen. Das angefügte Goal führt das gleiche aus, wie schondas Goal assembly. Um das Goal assembly:attached der Phase package zuzuordnenbenutzen wir das Element executions innerhalb des Elements plugin der Sektionbuild des Projekt POMs.

Example 4.20. Anbinden des Assembly Goals zur Packetierungs Phase

<project>[...]<build>

<plugins><plugin>

<artifactId>maven-assembly-plugin</artifactId><configuration>

<descriptorRefs><descriptorRef>jar-with-dependencies</descriptorRef>

</descriptorRefs></configuration>

</plugin><executions>

<execution><id>simple-command</id><phase>package</phase><goals><goal>attached</goal>

</goals></execution>

</executions></plugins>

</build>[...]

</project>

Sobald Sie diese Änderung Ihres POMs angefügt haben müssen Sie nur noch mvnpackage aufrufen, um Ihr Assembly zu generieren. Die Konfiguration derexecution wird sicherstellen, dass das Goal assembly:attached ausgeführt wird,sobald Maven in die Lebenszyklusphase package eintritt.

Anpassen eines Maven Projektes

90

Page 110: Maven Definitive Guide De

Chapter 5. Eine einfache Web-Anwendung

5.1. EinleitungIn diesem Kapitel erstellen wir mit Hilfe des Archetype Maven Plugin eineeinfache Web-Anwendung. Wir führen diese Web-Applikation ein, benutzenhierfür einen Servlet-Container Jetty, fügen einige Abhängigkeiten ein, schreibenein einfaches Servlet und erzeugen ein WAR-Archiv. Am Ende dieses Kapitels,werden Sie in der Lage sein Maven zur Beschleunigung der Entwicklung vonWeb-Applikationen einzusetzen.

5.1.1. Herunterladen der Beispiele dieses KapitelsDas Beispiel in diesem Kapitel wird mit dem Archetype Maven Plugin erzeugt.Während Sie in der Lage sein sollten, der Entwicklung dieses Kapitels ohneBeispiel-Quellcode zu folgen, empfehlen wir das Herunterladen einer Kopie derQuellcodes als Referenz. Das Beispielprojekt dieses Kapitel zusammen mit denanderen Beispielen dieses Buchs kann vonhttp://books.sonatype.com/maven-book/mvn-examples-1.0.zip oderhttp://books.sonatype.com/maven-book/mvn-examples-1.0.tar.gz heruntergeladenwerden. Entpacken Sie das Archiv in ein beliebiges Verzeichnis, und gehen Siedann zum Verzeichnis /ch05. Darin finden Sie ein Verzeichnis mit dem Namen/simple web welches den Quellcode für dieses Kapitel enthält.

5.2. Eine kurze Einführung in die "SimpleWeb" AnwendungWir haben dieses Kapitel gezielt auf Plain-Old Web Applikationen (POWA)konzentriert - ein Servlet und eine JSP-Seite. Wir werden Ihnen in den nächsten 20Seiten sicher nicht zeigen wie Sie Ihre Struts 2, Tapestry, Wicket, JSF, oder WaffleAnwendung erstellen, auch nicht wie Sie die Integration eines IoC Containers wie

91

Page 111: Maven Definitive Guide De

Plexus, Guice oder Spring Framework bewerkstelligen. Das Ziel dieses Kapitels istes, Ihnen die grundlegenden Möglichkeiten welche Maven zur Entwicklung vonWeb-Anwendungen bietet vorzustellen. Nicht mehr und nicht weniger! Später indiesem Buch, werden wir einen Blick auf die Entwicklung von zweiWeb-Anwendungen werfen: einer, welche Hibernate, Velocity sowie das SpringFramework nutzt, und eine andere unter Verwendungen von Plexus.

5.3. Erstellen des "Simple Web" ProjektsUm das Web Applikationsprojekt zu erstellen, führen Sie mvn archetype:createmit einer artifactId und einer groupId aus. Wählen Sie diearchetypeArtifactId maven-archetype-webapp. Bei der Ausführung wird dieentsprechende Verzeichnis-Struktur und das grundlegende Maven POM erstellt.

~/examples$ mvn archetype:create -DgroupId=org.sonatype.mavenbook.ch05 \-DartifactId=simple-webapp \-DpackageName=org.sonatype.mavenbook \-DarchetypeArtifactId=maven-archetype-webapp

[INFO] [archetype:create][INFO] ----------------------------------------------------------------------[INFO] Using parameters for creating Archetype: maven-archetype-webapp:RELEASE[INFO] ----------------------------------------------------------------------[INFO] Parameter: groupId, Value: org.sonatype.mavenbook.ch05[INFO] Parameter: packageName, Value: org.sonatype.mavenbook[INFO] Parameter: basedir, Value: ~/examples[INFO] Parameter: package, Value: org.sonatype.mavenbook[INFO] Parameter: version, Value: 1.0-SNAPSHOT[INFO] Parameter: artifactId, Value: simple-webapp[INFO] *************** End of debug info from resources from generated POM **[INFO] Archetype created in dir: ~/examples/simple-webapp

Sobald das Archetype Maven Plugin das Projekt erstellt hat, gehen Sie in dasVerzeichnis /simple-web und werfen Sie einen Blick auf die dort abgelegtepom.xml-Datei. Sie sollten das folgenden XML-Dokument vorfinden:

Example 5.1. Erstes POM des "Simple Web" Projekt

<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.0http://maven.apache.org/maven-v4_0_0.xsd">

<modelVersion>4.0.0</modelVersion>

Eine einfache Web-Anwendung

92

Page 112: Maven Definitive Guide De

<groupId>org.sonatype.mavenbook.ch05</groupId><artifactId>simple-webapp</artifactId><packaging>war</packaging><version>1.0-SNAPSHOT</version><name>simple-webapp Maven Webapp</name><url>http://maven.apache.org</url><dependencies>

<dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>3.8.1</version><scope>test</scope>

</dependency></dependencies><build>

<finalName>simple-webapp</finalName><plugins><plugin>

<artifactId>maven-compiler-plugin</artifactId><configuration>

<source>1.5</source><target>1.5</target>

</configuration></plugin>

</plugins></build>

</project>

Beachten Sie dass das Element packaging den Wert 'war' enthält. Ein Projekt desTypes war package, wird eine WAR-Datei im Zielverzeichnis erstellen, dasstandardmäßig den Namen ${artifactId}-${version}.war trägt. Für diesesProjekt, wird das standardmäßige WAR in/target/simple-webapp-1.0-SNAPSHOT.war abgelegt. Im "Simple Web" Projekthaben wir den Namen der WAR-Datei vorbestimmt, indem wir ein ElementfinalName in die Projekt Build Konfiguration eingefügt haben. Mit dem finalNamevon 'simple-webapp' generiert die Phase package ein WAR-Archiv in/target/simple-webapp.war.

5.4. Konfigurieren des Jetty-PluginsSobald Sie Ihre Web Anwendung kompiliert, getestet und verpackt haben, werdenSie sie wahrscheinlich einen Servlet-Container einsetzen wollen und diese mit der

Eine einfache Web-Anwendung

93

Page 113: Maven Definitive Guide De

vom Archetype Maven Plugin erstellten index.jsp-Datei testen wollen.Normalerweise wäre der Ablauf hierzu entsprechend: Herunterladen einesContainers wie etwas Jetty oder Apache Tomcat, Auspacken einer Distribution,Kopieren Ihres Anwendungs WAR-Archivs in ein /webapp-Verzeichnis,schliesslich starten des Containers.

Obschon man das alles tun kann, gibt es keine Notwendigkeit, dies zu tun.Stattdessen können Sie das Jetty Maven Plugin einsetzen, und IhreWeb-Applikation aus Maven heraus starten. Um dies zu tun, müssen wir das JettyMaven Plugin in unserem Projekt POM konfigurieren. Fügen Sie das folgendePlugin-Element Ihrer Projekt Build Konfiguration an:

Example 5.2. Konfigurieren des Jetty-Plugins

<project>[...]<build>

<finalName>simple-webapp</finalName><plugins><plugin>

<groupId>org.mortbay.jetty</groupId><artifactId>maven-jetty-plugin</artifactId>

</plugin></plugins>

</build>[...]

</project>

Nachdem Sie das Jetty Plugin in der Projekt pom.xml-Datei konfiguriert haben.können Sie das Goal jetty:run aufrufen, um Ihre Web Anwendung im JettyContainer zu starten. Rufen sie mvn jetty:run wie folgt auf:

~/examples$ mvn jetty:run...[INFO] [jetty:run][INFO] Configuring Jetty for project: simple-webapp Maven Webapp[INFO] Webapp source directory = \

~/svnw/sonatype/examples/simple-webapp/src/main/webapp[INFO] web.xml file = \

~/svnw/sonatype/examples/simple-webapp/src/main/webapp/WEB-INF/web.xml[INFO] Classes = ~/svnw/sonatype/examples/simple-webapp/target/classes2007-11-17 22:11:50.532::INFO: Logging to STDERR via org.mortbay.log.StdErrLog[INFO] Context path = /simple-webapp[INFO] Tmp directory = determined at runtime

Eine einfache Web-Anwendung

94

Page 114: Maven Definitive Guide De

[INFO] Web defaults = org/mortbay/jetty/webapp/webdefault.xml[INFO] Web overrides = none[INFO] Webapp directory = \

~/svnw/sonatype/examples/simple-webapp/src/main/webapp[INFO] Starting jetty 6.1.6rc1 ...2007-11-17 22:11:50.673::INFO: jetty-6.1.6rc12007-11-17 22:11:50.846::INFO: No Transaction manager found2007-11-17 22:11:51.057::INFO: Started [email protected]:8080[INFO] Started Jetty Server

Nach erfolgreichem Starten des Servlet-Containers durch Maven, laden Sie dieURL http://localhost:8080/simple-webapp/ in einen Web-Browser. Die durchArchetype generierte index.jsp-Datei ist trivial: sie enthält eine Überschrift mitdem Text: "Hallo Welt!". Maven erwartet, dass das Document Root derWeb-Applikation in /src/main/webapp liegt. In diesem Verzeichnis finden Sieauch die Datei index.jsp welche im Beispiel 5.3: "Inhalt von/src/main/webapp/index.jsp" aufgelistet wird.

Example 5.3. Inhalt von /src/main/webapp/index.jsp

<html><body>

<h2>Hello World!</h2></body>

</html>

Unter /src/main/webapp/WEB-INF finden Sie den kleinsten möglichenWeb-Applikations Beschrieb; der web.xml.

Example 5.4. Inhalt von src/main/webapp/WEB-INF/web.xml

<!DOCTYPE web-app PUBLIC"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN""http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app><display-name>Archetype Created Web Application</display-name>

</web-app>

Eine einfache Web-Anwendung

95

Page 115: Maven Definitive Guide De

5.5. Das Hinzufügen eines einfachen ServletsEine Web-Applikation mit einer einzigen JSP-Seite und keinen konfiguriertenServlets ist so gut wie nutzlos. Lassen Sie uns ein einfaches Servlet hinzufügenund die entsprechenden Änderungen in der pom.xml- sowie der web.xml-Dateivornehmen. Zu Beginn müssen wir ein neues Package namenscom.sonatype.maven.web im Verzeichnis /src/main/java erstellen

$ mkdir -p src/main/java/org/sonatype/mavenbook/web$ cd src/main/java/org/sonatype/mavenbook/web

Sobald Sie dieses Package kreiert haben, wechseln Sie in das Verzeichnis(/src/main/java/com/sonatype/maven/web) und erstellen Sie eine KlasseSimpleServlet in einer Datei SimpleServlet.java, welche den folgendenQuellcode enthält.

Example 5.5. SimpleServlet Klasse

package org.sonatype.mavenbook.web;

import java.io.*;import javax.servlet.*;import javax.servlet.http.*;

public class SimpleServlet extends HttpServlet {public void doGet(HttpServletRequest request,

HttpServletResponse response)throws ServletException, IOException {

PrintWriter out = response.getWriter();out.println( "SimpleServlet Executed" );out.flush();out.close();

}}

Unsere Klasse SimpleServlet ist eben dies, ein einfaches Servlet, welches eineeinfache Nachricht auf den responseWriter schreibt. Um dieses Servlet IhrerWeb-Anwendung hinzuzufügen und auf einen Anfragepfad abzubilden, fügen Siedie folgenden Servlet-und Servlet-Mapping-Elemente zu Ihrer Projekt

Eine einfache Web-Anwendung

96

Page 116: Maven Definitive Guide De

web.xml-Datei an. Die web.xml-Datei finden Sie unter/src/main/webapp/WEB-INF.

Example 5.6. Mapping the Simple Servlet

<!DOCTYPE web-app PUBLIC"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN""http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app><display-name>Archetype Created Web Application</display-name><servlet>

<servlet-name>simple</servlet-name><servlet-class>org.sonatype.mavenbook.web.SimpleServlet</servlet-class>

</servlet><servlet-mapping>

<servlet-name>simple</servlet-name><url-pattern>/simple</url-pattern>

</servlet-mapping></web-app>

Nun ist alles vorhanden, um das Servlet zu testen, die Klasse befindet sich unter/src/main/java und die web.xml-Datei wurde aktualisiert. Bevor wir dasJetty-Plugin starten, kompilieren Sie Ihr Projekt, durch den Aufruf von mvncompile:

~/examples$ mvn compile...[INFO] [compiler:compile][INFO] Compiling 1 source file to ~/examples/ch05/simple-webapp/target/classes[INFO] ------------------------------------------------------------------------[ERROR] BUILD FAILURE[INFO] ------------------------------------------------------------------------[INFO] Compilation failure

/src/main/java/org/sonatype/mavenbook/web/SimpleServlet.java:[4,0] \package javax.servlet does not exist

/src/main/java/org/sonatype/mavenbook/web/SimpleServlet.java:[5,0] \package javax.servlet.http does not exist

/src/main/java/org/sonatype/mavenbook/web/SimpleServlet.java:[7,35] \cannot find symbolsymbol: class HttpServletpublic class SimpleServlet extends HttpServlet {

/src/main/java/org/sonatype/mavenbook/web/SimpleServlet.java:[8,22] \

Eine einfache Web-Anwendung

97

Page 117: Maven Definitive Guide De

cannot find symbolsymbol : class HttpServletRequestlocation: class org.sonatype.mavenbook.web.SimpleServlet

/src/main/java/org/sonatype/mavenbook/web/SimpleServlet.java:[9,22] \cannot find symbolsymbol : class HttpServletResponselocation: class org.sonatype.mavenbook.web.SimpleServlet

/src/main/java/org/sonatype/mavenbook/web/SimpleServlet.java:[10,15] \cannot find symbolsymbol : class ServletExceptionlocation: class org.sonatype.mavenbook.web.SimpleServlet

Die Erstellung schlägt fehl, Ihr Maven-Projekt weist keine Abhängigkeit zumServlet-API aus. Im nächsten Abschnitt werden wir diese hinzufügen und dasServlet-API in das Projekt POM eintragen.

5.6. Das Hinzufügen von J2EE-AbhängigkeitenUm ein Servlet zu schreiben benötigen Sie die Servlet-API-Spezifikation. Um dieServlet-API-Spezifikation als eine Abhängigkeit zu Ihrem Projekt POMhinzuzufügen, fügen Sie das folgende dependency Element Ihrer pom.xml an.

Example 5.7. Einfügen der Servlet-Spezifikation 2.4 als Abhängigkeit

<project>[...]<dependencies>

[...]<dependency><groupId>javax.servlet</groupId><artifactId>servlet-api</artifactId><version>2.4</version><scope>provided</scope>

</dependency></dependencies>[...]

</project>

Es ist auch darauf hinzuweisen, dass wir für diese Abhängigkeit denGeltungsbereich provided verwendet haben. Dies sagt Maven, dass das Archiv

Eine einfache Web-Anwendung

98

Page 118: Maven Definitive Guide De

seitens des Containers bereitgestellt wird, und nicht in das war-Archiv einfliessensoll. Wenn Sie daran interessiert waren, ein benutzerdefiniertes JSP-Tag für dieseseinfache Web-Anwendung zu erstellen, so bräuchten Sie eine Abhängigkeit zurJSP-Spezifikation 2.0. Verwenden Sie die folgende Konfiguration, um dieseAbhängigkeit zu schaffen.

Example 5.8. Hinzufügen der 2.0 JSP-Spezifikation als Abhängigkeit

<project>[...]<dependencies>

[...]<dependency><groupId>javax.servlet.jsp</groupId><artifactId>jsp-api</artifactId><version>2.0</version><scope>provided</scope>

</dependency></dependencies>[...]

</project>

Sobald Sie die Servlet Spezifikationsimplementierung als Abhängigkeithinzugefügt haben, führen Sie mvn clean install gefolgt von mvn jetty:run aus.

[tobrien@t1 simple-webapp]$ mvn clean install...[tobrien@t1 simple-webapp]$ mvn jetty:run[INFO] [jetty:run]...2007-12-14 16:18:31.305::INFO: jetty-6.1.6rc12007-12-14 16:18:31.453::INFO: No Transaction manager found2007-12-14 16:18:32.745::INFO: Started [email protected]:8080[INFO] Started Jetty Server

An diesem Punkt sollten Sie in der Lage sein, die Ausgabe des SimpleServlet zuerhalten. Von der Befehlszeile können Sie mittels curl die Ausgabe des Servlet zurStandard-Ausgabe umleiten:

~/examples$ curl http://localhost:8080/simple-webapp/simpleSimpleServlet Executed

Eine einfache Web-Anwendung

99

Page 119: Maven Definitive Guide De

5.7. ZusammenfassungNach dem Lesen dieses Kapitels sollten Sie in der Lage sein eine einfacheWeb-Applikation zu erstellen. In diesem Kapitel wurde nicht näher auf dieMillionen verschiedener Möglichkeiten eingegangen wie man eine kompletteWeb-Anwendung erschafft. Weitere Kapitel werden einen umfassenderenÜberblick über Projekte geben, bei denen einige der populären Web-Frameworksund Technologien zum Einsatz kommen.

Eine einfache Web-Anwendung

100

Page 120: Maven Definitive Guide De

Chapter 6. Ein multi-modulares Projekt

6.1. EinleitungIn diesem Kapitel werden wir ein Multi-Modul Projekt erstellen, welches dieBeispiele der beiden vorangegangenen Kapitel verbindet. Die "Simple Weather"Anwendung von Chapter 4, Anpassen eines Maven Projektes (Kapitel 4:"Anpassen eines Maven Projekts") wird verbunden mit der "Simple Web"Applikation von Chapter 5, Eine einfache Web-Anwendung (Kapitel 5: "Eineeinfache Web-Anwendung"), mit dem Ergebnis einer Web Anwendung welche dieWetterdaten zu einer gegebenen Postleitzahl abruft und die Wettervorhersage aufeiner Webseite darstellt. Am Ende dieses Kapitels werden Sie in der Lage sein,unter Verwendung von Maven komplexe, multi-modulare Projekte zu entwickeln.

6.1.1. Herunterladen der Beispiele dieses KapitelsDas multi-modulare Projekt welches wir in diesem Kapitel erstellen werden,besteht aus modifizierten Versionen der Projekte der vorhergehenden Kapitel: 4(Kapitel 4: "Anpassen eines Maven Projekts") sowie 5 (Kapitel 5: "Eine einfacheWeb-Anwendung"). Wir werden dieses multi-modulare Projekt nicht mit demArchetype Maven Plugin generieren. Wir empfehlen Ihnen eine Kopie derBeispiel-Code als zusätzliche Referenz beim Lesen der Inhalte in diesem Kapitelherunterzuladen. Das Beispielprojekt dieses Kapitels zusammen mit den anderenBeispielen aus diesem Buch kann vonhttp://books.sonatype.com/maven-book/mvn-examples-1.0.zip oderhttp://books.sonatype.com/maven-book/mvn-examples-1.0.tar.gz heruntergeladenwerden. Entpacken Sie das Archiv in ein beliebiges Verzeichnis, und gehen Siedann zum Verzeichnis /ch06. Darin finden Sie ein Verzeichnis mit dem Namen/simple-parent welches das multi-modulare Projekt dieses Kapitels enthält.Innerhalb des Verzeichnisses /simple-parent werden Sie auf eine pom.xml-Dateisowie zwei Unterverzeichnisse /simple weather und /simple web app stossenwelche den Quellcode für dieses Kapitel enthalten.

101

Page 121: Maven Definitive Guide De

6.2. Das "Simple Parent" Projekt(parent=Elternteil)Ein multi modulares Projekt wird durch ein "Eltern"-POM und dessenreferenzierende Submodule definiert. Im Verzeichnis /simple-parent finden Siedie "Eltern" POM Datei pom.xml, auch das 'top-level' POM genannt,

Example 6.1. simple-parent Projekt POM

<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/maven-v4_0_0.xsd"><modelVersion>4.0.0</modelVersion>

<groupId>org.sonatype.mavenbook.ch06</groupId><artifactId>simple-parent</artifactId><packaging>pom</packaging><version>1.0</version><name>Chapter 6 Simple Parent Project</name>

<modules><module>simple-weather</module><module>simple-webapp</module>

</modules>

<build><pluginManagement><plugins>

<plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><configuration><source>1.5</source><target>1.5</target>

</configuration></plugin>

</plugins></pluginManagement></build>

<dependencies><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>3.8.1</version><scope>test</scope>

Ein multi-modulares Projekt

102

Page 122: Maven Definitive Guide De

</dependency></dependencies>

</project>

Wie Sie sehen, definiert das top-level POM die Maven Koordinaten groupId alscom.sonatype.maven, artifactId als simple-parent und version als 1.0. Dastop level POM definiert keinen Ausgabetyp JAR- oder WAR-Archiv wie dasbislang der Fall, sondern bezieht sich auf andere Maven Projekte. Dasentsprechende Element package eines top level POM welches lediglich ein ProjektObjekt Modell darstellt, ist 'pom'. Der nächste Abschnitt in der pom.xml-Datei führtdie Sub-Module auf. Diese Module werden im Element module aufgeführt, undbefinden sich je in einem eigenen Unterverzeichnis. Maven kennt diese Strukturund sucht in den Unterverzeichnissen nach weiteren pom.xml-Dateien. Diesewerden dann in die Liste der Maven Projekte des Builds aufgenommen.

Zuletzt setzen wir eine Anzahl Einstellungen welche von allen Untermodulengeerbt werden. Das "Simple Parent" Modul setzt die Zielplattform allerUntermodule als Java 5 Plattform. Da das Compiler-Plugin standardmässig an denLebenszyklus gekettet ist, können wir den Abschnitt pluginManagement hierzubenutzen. Wir werden pluginManagement später im Detail beleuchten, für denMoment ist es einfacher, diese Trennung zwischen Bereitstellung von PluginStandard-Konfigurationen und dem tatsächlichen 'binden' der Plugins darzustellenwenn sie auf diese Weise getrennt werden. Das Element dependency fügt alsglobale Abhängigkeiten JUnit 3.8.1 ein. Sowohl die Konfiguration sowie dieAbhängigkeiten werden von allen Untermodulen geerbt. Der Einsatz der POMVererbung erlaubt es Ihnen, gemeinsam genutzte Abhängigkeiten, wie z.B. JUnitoder Log4J, universell zu definieren.

6.3. Das "Simple Weather" ModulDas erste Untermodul welches wir uns genauer ansehen wollen, ist dasUntermodul "Simple Weather". Dieses Modul enthält alle Klassen, welche zumAbrufen und Verarbeiten des Yahoo! Wetterdienstes benötigt werden.

Ein multi-modulares Projekt

103

Page 123: Maven Definitive Guide De

Example 6.2. POM des Untermodul "Simple-Weather"

<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/maven-v4_0_0.xsd"><modelVersion>4.0.0</modelVersion><parent>

<groupId>org.sonatype.mavenbook.ch06</groupId><artifactId>simple-parent</artifactId><version>1.0</version>

</parent><artifactId>simple-weather</artifactId><packaging>jar</packaging>

<name>Chapter 6 Simple Weather API</name>

<build><pluginManagement><plugins>

<plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-surefire-plugin</artifactId><configuration><testFailureIgnore>true</testFailureIgnore>

</configuration></plugin>

</plugins></pluginManagement>

</build>

<dependencies><dependency><groupId>log4j</groupId><artifactId>log4j</artifactId><version>1.2.14</version>

</dependency><dependency><groupId>dom4j</groupId><artifactId>dom4j</artifactId><version>1.6.1</version>

</dependency><dependency><groupId>jaxen</groupId><artifactId>jaxen</artifactId><version>1.1.1</version>

</dependency><dependency><groupId>velocity</groupId><artifactId>velocity</artifactId><version>1.5</version>

</dependency>

Ein multi-modulares Projekt

104

Page 124: Maven Definitive Guide De

<dependency><groupId>org.apache.commons</groupId><artifactId>commons-io</artifactId><version>1.3.2</version><scope>test</scope>

</dependency></dependencies>

</project>

Im POM des Moduls "Simple Weather" sehen wir, wie dieses Modul dieelterlichen Koordinaten in Form von Maven Koordianten referenziert. Daselterliche POM enthielt die Koordinaten groupId com.sonatype.maven,artifactId simple-parent sowie version 1.0 Beachten Sie hier, dass wir wederdie Koordinate groupID noch version definieren müssen, diese sind bereits im"elterlichen" POM definiert.

Example 6.3. Die Klasse WeatherService

package org.sonatype.mavenbook.weather;

import java.io.InputStream;

public class WeatherService {

public WeatherService() {}

public String retrieveForecast( String zip ) throws Exception {// Retrieve DataInputStream dataIn = new YahooRetriever().retrieve( zip );

// Parse DataWeather weather = new YahooParser().parse( dataIn );

// Format (Print) Datareturn new WeatherFormatter().format( weather );

}}

Die Klasse WeatherService ist unter/src/main/java/de/sonatype/Maven/Weather definiert, und ruft lediglich diedrei in Chapter 4, Anpassen eines Maven Projektes (Kapitel 4: "Anpassen einesMaven Projekts") definierten Objekte auf. Im Beispiel dieses Kapitels, erstellen

Ein multi-modulares Projekt

105

Page 125: Maven Definitive Guide De

wir ein separates Projekt, welches Service-Objekte bereitstellt, die vomWeb-Applikations Projekt angesprochen werden. Dies ist ein gewöhnliches Patternvon Enterprise Anwendungen. Oftmals besteht eine komplexe Anwendung ausweit mehr als einer einzigen (einfachen) Web-Applikation. Eine solcheAnwendung könnte aus einer Reihe von Web Anwendungen sowie weiterenBefehlszeilenanwendungen bestehen. Sie werden in diesem Fall die gemeinsameLogik in einer Service Klasse zusammenfassen und für eine Anzahl Projekte alsDienst bereitstellen. Dies gibt uns eine Rechtfertigung für die Erstellung einerKlasse WeatherService. Sie können daran sehen, wie die "Simple Web"Anwendung auf die bereitgestellten Service Objekte welche in "Simple Weather"bereitgestellt werden zugreift.

Die Methode retrieveForecast() hat einen Parameter Postleitzahl. Dieser wirdan die Methode retrieve() der Klasse YahooRetriever gegeben, welche dasentsprechende XML-Fragment beim Yahoo Wetter Dienst einholt. Das erhalteneXML wird an die Methode parse() übergeben, welche ein Objekt Weatherzurückgibt. Das Objekt Weather wird dann in Form einer lesbaren, formatiertenZeichenkette ausgegeben.

6.4. Das "Simple-Web" Anwendungs-ModulDas Untermodul "Simple Web" ist das zweite, referenzierte Sub-Modul desProjekts. Das darin enthaltene Web-Anwendungsprojekt ist vom UntermodulWeatherService abhängig und es enthält einige einfache Servlets zur Präsentationder Ergebnisse der Yahoo! Wetterdienst Abfrage.

Example 6.4. POM des Untermodul "Simple-Webapp"

<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.0http://maven.apache.org/maven-v4_0_0.xsd">

<modelVersion>4.0.0</modelVersion><parent>

<groupId>org.sonatype.mavenbook.ch06</groupId><artifactId>simple-parent</artifactId><version>1.0</version>

</parent>

Ein multi-modulares Projekt

106

Page 126: Maven Definitive Guide De

<artifactId>simple-webapp</artifactId><packaging>war</packaging><name>simple-webapp Maven Webapp</name><dependencies>

<dependency><groupId>javax.servlet</groupId><artifactId>servlet-api</artifactId><version>2.4</version><scope>provided</scope>

</dependency><dependency><groupId>org.sonatype.mavenbook.ch06</groupId><artifactId>simple-weather</artifactId><version>1.0</version>

</dependency></dependencies><build>

<finalName>simple-webapp</finalName><plugins><plugin>

<groupId>org.mortbay.jetty</groupId><artifactId>maven-jetty-plugin</artifactId>

</plugin></plugins>

</build></project>

Das "Simple Web" Modul definiert ein einfaches Servlet, welches eine Postleitzahlaus einem HTTP-Request ausliest, den weatherService aus Example 6.3, “DieKlasse WeatherService” (Beispiel 6.3: "Die klasse WeatherService") aufruft, unddas Resultat auf den Writer schreibt.

Example 6.5. Simple-Webapp WeatherServlet

package org.sonatype.mavenbook.web;

import org.sonatype.mavenbook.weather.WeatherService;import java.io.*;import javax.servlet.*;import javax.servlet.http.*;

public class WeatherServlet extends HttpServlet {public void doGet(HttpServletRequest request,

HttpServletResponse response)throws ServletException, IOException {String zip = request.getParameter("zip" );WeatherService weatherService = new WeatherService();

Ein multi-modulares Projekt

107

Page 127: Maven Definitive Guide De

PrintWriter out = response.getWriter();try {

out.println( weatherService.retrieveForecast( zip ) );} catch( Exception e ) {

out.println( "Error Retrieving Forecast: " + e.getMessage() );}out.flush();out.close();

}}

Im WeatherServlet, instanziieren wir die Klasse "SimpleWeather" desSimpleWeather Projekts. Die Postleitzahl welche in den Anfrage-Parameternübermittelt wird, geht an die Methode retrieveForecast(), der zurückgegebeneText wird auf den Writer geleitet. Schlussendlich besteht die web.xml-Datei der"Simple Web" Anwendung, welche unter /src/main/webapp/WEB-INF zu findenist, und alles zusammenbindet. Die Servlet-und Servlet-Mapping-Elementedefinieren hierbei einen Pfad von /weather auf das WeatherServlet.

Example 6.6. web.xml der Simple-Webapp

<!DOCTYPE web-app PUBLIC"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN""http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app><display-name>Archetype Created Web Application</display-name><servlet>

<servlet-name>simple</servlet-name><servlet-class>org.sonatype.mavenbook.web.SimpleServlet</servlet-class>

</servlet><servlet>

<servlet-name>weather</servlet-name><servlet-class>org.sonatype.mavenbook.web.WeatherServlet</servlet-class>

</servlet><servlet-mapping>

<servlet-name>simple</servlet-name><url-pattern>/simple</url-pattern>

</servlet-mapping><servlet-mapping>

<servlet-name>weather</servlet-name><url-pattern>/weather</url-pattern>

</servlet-mapping></web-app>

Ein multi-modulares Projekt

108

Page 128: Maven Definitive Guide De

6.5. Erstellung des Multi-Projekt-ModulsMit dem "Simple Weather" Projekt welches den allgemeinen Code zur Interaktionmit dem Yahoo! Wetterdienst erstellt, sowie der "Simple Web" Anwendung welchedie Webanwendung in Form eines Servlets bereitstellt, es ist an der Zeit, zuKompilieren und Packetieren, sowie Bereitstellen der Anwendung in Form einesWAR-Archivs. Um das zu bewerkstelligen, müssen Sie die beiden Projekte in derrichtigen Reihenfolge kompilieren und installieren. Da "Simple Web" von "SimpleWeather" abhängig ist, muss diese zuerst kompiliert werden und als JAR-Archivebereitgestellt werden. Tun Sie dies durch den Aufruf von mvn clean install ausdem Verzeichnis der Mutterapplikation heraus auf der Befehlszeile.

~/examples/ch06/simple-parent$ mvn clean install[INFO] Scanning for projects...[INFO] Reactor build order:[INFO] Simple Parent Project[INFO] simple-weather[INFO] simple-webapp Maven Webapp[INFO] ----------------------------------------------------------------------[INFO] Building simple-weather[INFO] task-segment: [clean, install][INFO] ----------------------------------------------------------------------[...][INFO] [install:install][INFO] Installing simple-weather-1.0.jar to simple-weather-1.0.jar[INFO] ----------------------------------------------------------------------[INFO] Building simple-webapp Maven Webapp[INFO] task-segment: [clean, install][INFO] ----------------------------------------------------------------------[...][INFO] [install:install][INFO] Installing simple-webapp.war to simple-webapp-1.0.war[INFO][INFO] ----------------------------------------------------------------------[INFO] Reactor Summary:[INFO] ----------------------------------------------------------------------[INFO] Simple Parent Project ............................... SUCCESS [3.041s][INFO] simple-weather ...................................... SUCCESS [4.802s][INFO] simple-webapp Maven Webapp .......................... SUCCESS [3.065s][INFO] ----------------------------------------------------------------------

Wenn Maven gegen ein Projekt mit Submodulen ausgeführt wird, lädt Mavenzunächst das höchststehende POM, dann lokalisiert es alle abhängigenPOM-Dateien. Maven bringt alle Komponenten POM-Inhalte in das, was man den

Ein multi-modulares Projekt

109

Page 129: Maven Definitive Guide De

Maven Reaktor nennt, innerhalb werden die Komponenten POM auf derenAbhängigkeiten analysiert. Der Reaktor definiert die Reihenfolge derKomponenten und stellt deren Abarbeitung und Installation sicher.

NoteDer Reaktor wird die Reihenfolge der Module gemäß der Definition inder POM erhalten, soweit dies möglich ist. Eine hilfreiches mentalesModell hierfür ist das Bild, dass Module mit Abhängigkeiten vonGeschwister-Projekten solange in der Reihenfolge "nach hinten" gerücktwerden, bis deren Abhängigkeitsverhältnisse befriedigt sind. In seltenenFällen kann es nützlich sein, in die Reihenfolge der Module einzugreifen,- zum Beispiel, wenn Sie ein häufig instabiles Modul gegen Anfang desBuild abarbeiten möchten.

Nachdem der Reaktor ausgearbeitet hat, in in welcher Reihenfolge die Projekteabgearbeitet werden müssen, führt Maven die angegebenen Goals für diejeweiligen Module des Multi-Moduls aus. In diesem Beispiel können Sie erkennen,wie Maven "Simple Weather" vor "Simple Web" stellt, und auf dem jeweiligemModul einen Befehl mvn clean install abarbeitet.

NoteBeim Ausführen von Maven von der Befehlszeile sollten Sie dieLebenszyklusphase clean vor allen anderen Phasen spezifizieren. Mitdem Aufruf von clean stellen Sie sicher, dass alle alten Output-Artefaktegelöscht werden, vor die Anwendung kompiliert und paketiert wird.clean auszuführen ist nicht notwendig, aber es stellt sicher dass Sie einen"sauberen Build" erstellen.

6.6. Starten der Web-AnwendungNach der Installation des Multi-Modul-Projekts mittels mvn clean install aus demStammverzeichnis von "Simple Project" heraus, können Sie direkt in das

Ein multi-modulares Projekt

110

Page 130: Maven Definitive Guide De

Unterverzeichnis /Simple Web wechseln und dort das Goal run des Jetty Pluginsaufrufen:

~/examples/ch06/simple-parent/simple-webapp $ mvn jetty:run[INFO] ----------------------------------------------------------------------[INFO] Building simple-webapp Maven Webapp[INFO] task-segment: [jetty:run][INFO] ----------------------------------------------------------------------[...][INFO] [jetty:run][INFO] Configuring Jetty for project: simple-webapp Maven Webapp[...][INFO] Webapp directory = ~/examples/ch06/simple-parent/\

simple-webapp/src/main/webapp[INFO] Starting jetty 6.1.6rc1 ...2007-11-18 1:58:26.980::INFO: jetty-6.1.6rc12007-11-18 1:58:26.125::INFO: No Transaction manager found2007-11-18 1:58:27.633::INFO: Started [email protected]:8080[INFO] Started Jetty Server

Sobald Jetty mit dem Startvorgang abgeschlossen hat, laden Siehttp://localhost:8080/simple-webapp/weather?zip=01201 in einen Browser und Siesollten sehen, wie die Wettervorhersage formatiert ausgegeben wird.

Ein multi-modulares Projekt

111

Page 131: Maven Definitive Guide De

Chapter 7. Multi-module Enterprise Project

7.1. EinleitungIn diesem Kapitel werden wir, aufbauend auf die Beispiele der Kapitel 6: EinMulti-Projekt-Modul sowie Kapitel 5. Eine einfache Web-Anwendung, einMulti-Modul-Projekt entwickeln. In dieses Beispiel werden wir das SpringFramework und auch Hibernate einbinden, um eine einfache Web-Anwendung undgleichzeitig ein einfaches Befehlszeilen-Dienstprogramm zum Lesen von Datendes Yahoo! Wetter Dienstes zu erstellen. Den Quellcode des "Simple Weather"Beispiels aus Chapter 4, Anpassen eines Maven Projektes (Kapitel 4: Anpasseneines Maven Projekts) werden wir mit dem Quellcode aus Chapter 5, Eine einfacheWeb-Anwendung (Kapitel 5: Eine einfache Web-Anwendung) kombinieren. ImLaufe der Entwicklung dieses Multi Modul Projekts werden wir Fragenstellungenbezüglich des Einsatzes von Maven erörtern und verschiedene Möglichkeiten derUmsetzung darlegen, um den Aufbau modularer Projekte zu fördern und dieWiederverwendung von Komponenten aufzuzeigen.

7.1.1. Herunterladen der Beispiele dieses KapitelsDas multi-modulare Projekt welches wir in diesem Kapitel erstellen werden,besteht aus modifizierten Versionen der Projekte der vorhergehenden Kapitel;Chapter 4, Anpassen eines Maven Projektes (Kapitel 4: Anpassen eines MavenProjekts) sowie Chapter 5, Eine einfache Web-Anwendung (Kapitel 5: Eineeinfache Web-Anwendung). Wir werden dieses multi-modulare Projekt nicht mitdem Archetype Maven Plugin generieren. Wir empfehlen Ihnen eine Kopie derBeispiel-Code als zusätzliche Referenz beim Lesen der Inhalte in diesem Kapitelherunterzuladen. Das Beispielprojekt dieses Kapitel zusammen mit den anderenBeispielen dieses Buchs kann vonhttp://www.sonatype.com/book/mvn-examples-1.0.zip oderhttp://books.sonatype.com/maven-book/mvn-examples-1.0.tar.gz heruntergeladenwerden. Entpacken Sie das Archiv in ein beliebiges Verzeichnis, und gehen Sie

112

Page 132: Maven Definitive Guide De

dann zum Verzeichnis /ch07. Darin finden Sie ein Verzeichnis mit dem Namen/simple-parent welches das multi modulare Projekt dieses Kapitels enthält.Innerhalb des Verzeichnisses /simple-parent werden Sie auf eine pom.xml-Dateisowie zwei Unterverzeichnisse /simple weather und /simple web app stossen welcheden Quellcode für dieses Kapitel enthalten.

7.1.2. Multi-Modul Enterprise ProjektDie Komplexität einer extensiven Enterprise Applikation aufzuzeigen würde denRahmen dieses Buches bei weitem sprengen. Solche Projekte sind gekennzeichnetdurch den Einsatz mehrerer Datenbanken, der Integration externer Systeme undTeilprojekte welche weiter z.B. nach Abteilungen aufgeteilt werden. SolcherleiProjekte beinhalten in der Regel tausende von Zeilen Quellcode, und reflektierendie Arbeit von Dutzenden oder Hunderten von Software-Entwicklern. Da einsolches Beispiel den Rahmen des Buches sprengen würde, können wir Ihnen hierlediglich anhand eines Beispielprojekts die Komplexitäten größererEnterprise-Anwendungen aufzeigen. Im Abschluss geben wir einige Ratschlägeder modularen Strukturierung welche über dieses Kapitel hinausgehen.

In diesem Kapitel wenden wir uns einem Multi Modul Projekt zu, welches zweiBenutzerschnittstellen definiert: ein Befehlszeilen-Abfrage-Tool für die Yahoo!Wetter-Dienste, sowie eine Web-Anwendung welche auf diese Dienste aufbaut.Beide Anwendungen speichern die Resultate der Abfragen in einer eingebettetenDatenbank. Beide Anwendungen erlauben es dem Benutzer auf historisierteAbfragewerte der eingebetteten Datenbank zuzugreifen. Beide Anwendungenteilen sich die Anwendungslogik und auch eine Persistenz Bibliothek. DiesesKapitel baut auf den Code zur Auswertung des Yahoo! Wetter Dienstes auf,welcher in Chapter 4, Anpassen eines Maven Projektes (Kapitel 4 Anpassen einesMaven Projekts) eingeführt wurde. Dieses Projekt gliedert sich in fünfUntermodule wie in Figure 7.1, “Beziehungen der Module einer komplexenEnterprise Anwendung” (Abbildung 7.1: "Beziehungen der Module einerkomplexen Enterprise Anwendung") veranschaulicht wird.

Multi-module Enterprise Project

113

Page 133: Maven Definitive Guide De

Figure 7.1. Beziehungen der Module einer komplexen Enterprise Anwendung

Aus Figure 7.1, “Beziehungen der Module einer komplexen EnterpriseAnwendung” (Abbildung 7.1: "Beziehungen der Module einer komplexenEnterprise Anwendung") sehen Sie, dass es fünf Untermodule des "Eltern"-Projektgibt. Diese sind:

simple-modelDieses Modul definiert ein einfaches Objektmodell, welches die vom Yahoo!Wetter-Dienst zurückgegebenen Daten abbildet. Dieses Objektmodell beinhaltetdie Objekte Weather, Condition, Atmosphere, Location und Wind

(Wetterlage, Zustand, Atmosphäre, Ort sowie Windrichtung). Beim parsen dervom Yahoo! Wetter Dienst zurückgegebenen XML-Antwort werden weather

Objekte erstellt, welche an die Anwendung weitergegeben werden. DiesesProjekt beinhaltet Objekte welche mit Hibernate 3 Annotations versehen sindum die Zuordnung eines jeden Objektes zu einem Datensatz in der Datenbanksicherzustellen.

Multi-module Enterprise Project

114

Page 134: Maven Definitive Guide De

simple-weatherDieses Modul enthält alle Logik welche erforderlich ist, um Daten des Yahoo!Wetter-Dienstes anzuziehen und das zurückgegebene XML auszuwerten. Ausdem zurückgegebenen XML werden anschliessend Objekte wie diese in"Simple Model" definiert wurden, erstellt. Das "Simple Weather"-Modul istabhängig vom Modul "Simple Model". "Simple Weather" stellt einen Dienstbereit ("WeatherService"), welcher sowohl von "Simple Command" wie auchvon "Simple Web" konsumiert wird.

simple-persistDieses Modul enthält einige Data Access Objekte (DAO). Konfiguriert, umWeather Objekte in einer eingebetteten Datenbank abzuspeichern. BeideBenutzerschnittstellen dieser multi-modularen Anwendung (Web/Befehlszeile)bauen auf einfache DAOs (Data Access Objekts=Datenzugriffsobjekte) auf,welche zur Speicherung (speichern=to persist) herangezogen werden. DieModellobjekte welche in "Simple Model" bereitgestellt werden, können vonden DAOs sowohl verstanden wie auch zurückgegeben werden. "SimplePersist" ist direkt von "Simple Model" abhängig und definiert weitereAbhängigkeiten zu den Modellobjekten, gekennzeichnet durch HibernateAnnotations.

simple-webappDas Web-Anwendungs Projekt definiert zwei SpringMVC-Controller-Implementierungen welche unter Verwendung desWeatherService definiert werden, sowie die DAOs aus "Simple Persist"."Simple Web" verfügt über eine direkte Abhängigkeit zu "Simple Weather"sowie eine transitive Abhängigkeit zu "Simple Model".

simple-commandDieses Modul enthält eine einfache Befehlszeilen-Anwendung welche dazueingesetzt werden kann, die Yahoo! Wetter-Dienste abzufragen. Das Modulenthält eine statisch definierte Klasse main() welche mit WeatherService aus"Simple Weather" sowie der DAOs welche in "Simple Persist" definiertwurden arbeitet. "Simple Command" hat eine direkte Abhängigkeit zu "Simple

Multi-module Enterprise Project

115

Page 135: Maven Definitive Guide De

Weather" sowie "Simple Persist"; es besteht eine transitive Abhängigkeit zu"Simple Model".

Hiermit ist das Beispiel in diesem Kapitel vorgestellt; es ist einfach genug um es ineinem Lehrbuch einzuführen, und doch komplex genug um eine Aufspaltung infünf Untermodule zu rechtfertigen. Während unser künstliches Beispiel einModell-Projekt von fünf Klassen, eine Persistenz-Bibliothek mit zweiService-Klassen und eine Parsing-Bibliothek mit fünf oder sechs Klassen hat, kannein tatsächliches "Reale-Welt-System" leicht ein Modell-Projekt mit hunderten vonKlassen, mehrere Persistenz- und Service-Bibliotheken welche über mehrereBereiche reichen, umfassen. Während wir versucht haben sicherzustellen, dass derQuellcode des Beispiels einfach genug ist, um diesen in einer Sitzung zu verstehen,haben wir uns ebenso bemüht ein modulares Beispiel zu erstellen. Es wärenachvollziehbar, würden Sie nach der Betrachtung des Quellcodes, sich vonMaven abwenden und den Eindruck mitnehmen, Maven ermuntere dazuKomplexität einzuführen, da das Beispiel lediglich fünf Klassen umfasst. Jedochsollten Sie immer bedenken, dass wir keine Mühen scheuten, das einfache Beispielanzureichern, um die Möglichkeiten zur Bearbeitung komplexer Fragestellungenbeispielhaft aufzuzeigen und darzustellen, insbesondere die von Maven gegebenenMulti-Modul-Funktionalitäten.

7.1.3. Technologie des BeispielsDie in diesem Kapitel eingesetzten Technologien umfassen solche, welche zwarpopulär sind, aber nicht in direktem Zusammenhang mit dem Einsatz von Mavenstehen. Die angedeuteten Technologien sind: das Spring Framework™ undHibernate™. Das Spring Framework stellt einen 'Inversion of Control (IoC)Container' bereit, sowie eine Reihe von Frameworks welche auf die Vereinfachungder Interaktion mit verschiedenen J2EE-Bibliotheken abzielen. Mit dem SpringFramework als Fundament der Anwendungsentwicklung erhalten Sie Zugriff aufeine Reihe von hilfreichen Abstraktionen, welche Ihnen einen Großteil dermühseligen Fummelei im Umgang mit z.B. Persistenz Frameworks wie Hibernateoder iBatis™ oder auch Enterprise-APIs wie JDBC, JNDI und JMS nehmen. DasSpring Framework hat in den vergangenen Jahren stark an Popularität gewonnen,insbesondere als Alternative zu den schwergewichtigen Enterprise Standards aus

Multi-module Enterprise Project

116

Page 136: Maven Definitive Guide De

dem Hause Sun Microsystems. Hibernate ist ein weit verbreitetesObject-Relational-Mapping- (ORM-) Framework, das eine Interaktion mitrelationalen Datenbanken bereitstellt, als ob es sich um eine Sammlung vonJava-Objekten handele. Dieses Beispiel konzentriert sich auf den Aufbau einereinfachen Web-Anwendung und eines Befehlszeilen-Programms, welche,aufbauend auf das Spring Framework und Hibernate, eine Reihe vonwiederverwendbaren Komponenten bereitstellt, welche es erlauben diebestehenden Wetter-Daten in einer eingebettete Datenbank abzulegen(persistieren).

Wir haben uns entschlossen diese Frameworks einzubinden, um Ihnen am Beispielaufzuzeigen, wie man beim Einsatz von Maven diese Technologien einbindenkann. Obschon es in das Kapitel eingestreut kurze Einführungen zu diesenTechnologien gibt, so ist es nicht das erklärte Ziel dieses Kapitels dieseTechnologien im Detail darzustellen. Bezüglich weiterführender Dokumentationdieser Frameworks verweisen wir: bezüglich Spring auf die Projekt Websitehttp://www.springframework.org, bezüglich Hibernate auf den Projekt Websitehttp://www.hibernate.org. Dieses Kapitel setzt als eingebettete Datenbank aufHSQLDB, weitere Informationen zu dieser Datenbank finden Sie auf der ProjektWebsite http://hsqldb.org.

7.2. Das "Simple Parent" Projekt - Top LevelDas "Simple Parent" Hauptmodul hat ein POM welches auf fünf Untermoduleverweist: (simple-command), (simple-model), (simple-weather), (simple-persist)und (simple-webapp). Das Top-Level POM (pom.xml-Datei) ist im Example 7.1,“Simple Parent POM Projekt” (Beispiel 7.1, "Simple Parent POM Projekt")dargestellt.

Example 7.1. Simple Parent POM Projekt

<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/maven-v4_0_0.xsd"><modelVersion>4.0.0</modelVersion>

Multi-module Enterprise Project

117

Page 137: Maven Definitive Guide De

<groupId>org.sonatype.mavenbook.ch07</groupId><artifactId>simple-parent</artifactId><packaging>pom</packaging><version>1.0</version><name>Chapter 7 Simple Parent Project</name>

<modules><module>simple-command</module><module>simple-model</module><module>simple-weather</module><module>simple-persist</module><module>simple-webapp</module>

</modules>

<build><pluginManagement><plugins>

<plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><configuration><source>1.5</source><target>1.5</target>

</configuration></plugin>

</plugins></pluginManagement></build>

<dependencies><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>3.8.1</version><scope>test</scope>

</dependency></dependencies>

</project>

NoteSollten Sie bereits mit Maven POMs vertraut sein, so werden Sie jetztbemerken, dass dieses top level POM kein ElementdependencyManagement definiert. Das Element dependencyManagementerlaubt es Ihnen, Abhängigkeitsversionen in einem einzigen top-levelPOM zu definieren und wird in Chapter 8, Optimirung und

Multi-module Enterprise Project

118

Page 138: Maven Definitive Guide De

Überarbeitung der POMs (Kapitel 8: Optimieren und Refaktorieren vonPOMs) eingeführt.

Bemerken Sie bitte die Ähnlichkeiten dieses Hauptmodul POMs zu jenemHauptmodul POM des Projekts in Example 6.1, “simple-parent Projekt POM”(Kapitel 6.1: "Simple Parent Projekt POM"). Der einzige, wirkliche Unterschiedzwischen diesen beiden POMs ist die Liste der Untermodule. Wo dasvorangegangene Beispiel nur zwei Untermodule aufführte, hat dieses Hauptmodulderen fünf. In den nächsten Abschnitten wird jedes dieser fünf Untermodule ineinigen Details dargestellt. Da unsere Beispiel Java-Annotationen einsetzt, habenwir es so konfiguriert, dass der Compiler auf die Java 5 Virtual Machine (JVM)aufsetzt.

7.3. Das Simple Model" Modul - DasObjektmodellDas erste, was die meisten Enterprise Projekte benötigen, ist ein Objekt-Modell.Ein Objekt-Modell erfasst die Grundmenge aller Domain-Objekte eines Systems.Ein Banken-System könnten ein Objekt-Modell haben, welches aus einem Konto,Kunden- und Transaktions-Objekt besteht, ein System zum Erfassen undVerbreiten von Sportergebnisse besässe ein Mannschafts- und ein Spiel-Objekt.Was auch immer Ihr Projekt umfasst, die Chancen stehen gut, dass Sie dieentsprechenden Konzepte Ihres Systems in einem Objekt-Modell erfasst haben.Eine gängige Praxis im Umgang mit Maven ist es, dieses Modul separateauszulagern um es fortan als häufig referenziertes Modul weiterzuführen. Inunserem System erfassen wir jede Abfrage des Yahoo! Wetter-Dienstes mit einemObjekt, das Objekt weather, welches weitere vier Objekte referenziert:Windrichtung, Chill und Geschwindigkeit werden in einem Objekt Wind erfasst.Standortdaten einschließlich der Postleitzahl, Stadt, Region und Land sind in einerKlasse Location zusammengefasst. Die atmosphärischen Bedingungen wie dieFeuchtigkeit, maximale Sichtbarkeit, Luftdruck, und ob der Druck steigt oder fällt,wird in einer Klasse Atmosphere gehalten. Eine textuelle Beschreibung der

Multi-module Enterprise Project

119

Page 139: Maven Definitive Guide De

Zustände, der Temperatur, und die Daten der Beobachtung werden einer KlasseCondition zusammengefasst.

Figure 7.2. Einfaches Objektmodell für Wetter-Daten

Die pom.xml-Datei für dieses schlichte Modell hat nur eineAbhängigkeitsdefinition welche der Erläuterung bedarf: Unser Objekt-Modell wirdmit Hibernate Annotations realisiert. Wir verwenden Annotations um dieModell-Objekte auf Tabellen einer relationalen Datenbank abzubilden. DieAbhängigkeit besteht zu org.hibernate: hibernate-annotaions: 3.3.0.ga.Werfen Sie einen Blick in diese pom.xml sowie einige der kommenden Beispieleworin dies verdeutlicht wird:

Example 7.2. pom.xml des Projektmoduls: simple-model

<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.0http://maven.apache.org/maven-v4_0_0.xsd">

<modelVersion>4.0.0</modelVersion><parent>

<groupId>org.sonatype.mavenbook.ch07</groupId><artifactId>simple-parent</artifactId>

Multi-module Enterprise Project

120

Page 140: Maven Definitive Guide De

<version>1.0</version></parent><artifactId>simple-model</artifactId><packaging>jar</packaging>

<name>Simple Object Model</name>

<dependencies><dependency><groupId>org.hibernate</groupId><artifactId>hibernate-annotations</artifactId><version>3.3.0.ga</version>

</dependency><dependency><groupId>org.hibernate</groupId><artifactId>hibernate-commons-annotations</artifactId><version>3.3.0.ga</version>

</dependency></dependencies>

</project>

Unter /src/main/java/de/sonatype/Maven/weather/model liegt Weather.javawelche das annotierte Wetter-Objekt-Modell beinhaltet. Das Weather-Objekt istein einfaches Java Bean. Das bedeutet, es werden private Member Variablengesetzt (id, location, condition, wind, amosphere und date) welche durch publicGetter und Setter-Methoden offengelegt werden. Dabei wird folgende Regelumgesetzt: gibt es ein String-Property mit dem Namen "name" so wird eine publicGetter-Methode ohne Argumente namens getName() gesetzt, sowie eine publicSetter Methode mit einem Argument setName(String name); analog für andereTypen. Während wir hier die Getter und Setter-Methode für das id-Propertyzeigen, haben wir die Mehrheit der anderen Properties ausgelassen um an dieserStelle einer Anzahl Bäume das Leben zu wahren.

Example 7.3. Annotiertes Wetter Modell Objekt

package org.sonatype.mavenbook.weather.model;

import javax.persistence.*;

import java.util.Date;

@Entity@NamedQueries({

@NamedQuery(name="Weather.byLocation",

Multi-module Enterprise Project

121

Page 141: Maven Definitive Guide De

query="from Weather w where w.location = :location")})public class Weather {

@Id@GeneratedValue(strategy=GenerationType.IDENTITY)private Integer id;

@ManyToOne(cascade=CascadeType.ALL)private Location location;

@OneToOne(mappedBy="weather",cascade=CascadeType.ALL)private Condition condition;

@OneToOne(mappedBy="weather",cascade=CascadeType.ALL)private Wind wind;

@OneToOne(mappedBy="weather",cascade=CascadeType.ALL)private Atmosphere atmosphere;

private Date date;

public Weather() {}

public Integer getId() { return id; }public void setId(Integer id) { this.id = id; }

// Alle weiteren getter/setter Methoden ausgelassen...}

In der Klasse Weather, setzen wir auf Hibernate Annotationen, um dem "simplepersist" Projekt Anleitung zu geben. Diese Annotationen werden von Hibernatebenutzt, um ein Objekt auf eine Tabelle einer relationalen Datenbank abzubilden.Während eine ausführliche Einführung in die Hibernate Annotationen weit überden Rahmen dieses Kapitels hinausgeht, hier eine kurze Erklärung für Neugierige:Die Annotation @Entity bezeichnet diese Klasse als zu persistierende Einheit, die@Table Annotation haben wir ausgelassen, Hibernate wird daher denKlassennamen als Tabellennamen einsetzen und so das Objekt Weather auf eineTabelle Weather abbilden. Die @NamedQueries Annotation definiert eine Abfrage,welche vom WeatherDAO aus dem "Simple Persist" Projekt verwendet wird. DieAbfragesprache, welche von der @NamedQuery Annotation benutzt wird istHibernate Query Language (HQL). Jede Variable wird mit Annotationen versehenwelche den Typ, die Spalte sowie jegliche Beziehungen der Spalte zu anderen

Multi-module Enterprise Project

122

Page 142: Maven Definitive Guide De

Spalten definiert:

Id

Das Property id wird mit @Id gekennzeichnet. Dies zeichnet das Property id alsdas Property aus, welches den Primärschlüssel bezüglich der Tabelle in derDatenbank trägt. Die Kennzeichnung @GeneratedValue bestimmt wie neuePrimärschlüssel generiert werden. Im Falle von id, bestimmen wir denGenerationType IDENTITY, welcher auf die Verwendung des Datenbankeigenen Generierungsalgoryhtmus aufsetzt.

Location

Jede Instanz eines Wetter-Objekts (Weather) ist an eine Instanz eines StandortObjekts (Location) gebunden. Ein Standort Objekt repräsentiert einePostleitzahl und die Kennzeichnung @ManyToOne stellt sicher, dassWetter-Objekte, welche an den selben Standort gebunden sind auch auf dieselbeInstanz zeigen. Das Attribut cascade der Annotation @ManyToOne stellt sicher,dass wir bei jedem persistieren des Wetter Objekts auch das zugehörigeStandort Objekt persistieren.

Condition, Wind, AtmosphereJedes dieser Objekte wird mit einer Kennzeichnung von @OneToOne und demCascadeType ALL abgebildet. Dies bedeutet, dass jedes Mal, wenn wir einWetter-Objekt speichern, eine Zeile in der Tabelle Weather, aber auch in denTabellen Condition, Wind und Atmosphere erzeugt wird.

Date

Das Property date ist nicht annotiert, das bedeutet, dass Hibernate in Bezug aufdie Spalten alle Standardwerte benutzt um diese Zuordung zu definieren. DerSpaltenname wird als date gesetzt, und der Datentyp der Spalte auf denentsprechenden Typ um dem date-Objekt zu entsprechen.

NoteSollten Sie ein Property in der Abbildung nicht berücksichtingen, sowürden Sie dieses Property mit @Transient annotieren.

Multi-module Enterprise Project

123

Page 143: Maven Definitive Guide De

Next, take a look at one of the secondary model objects, Condition, shown inExample 7.4, “'Condition' Modell Objekt des Projektes "simple-model"”. Thisclass also resides in src/main/java/org/sonatype/mavenbook/weather/model.

Als nächstes, schauen Sie sich das weiterführende Objekt Modell Condition an.Diese Klasse ist ebenfalls unter/src/main/java/de/sonatype/maven/weather/model abgelegt.

Example 7.4. 'Condition' Modell Objekt des Projektes "simple-model"

package org.sonatype.mavenbook.weather.model;

import javax.persistence.*;

@Entitypublic class Condition {

@Id@GeneratedValue(strategy=GenerationType.IDENTITY)private Integer id;

private String text;private String code;private String temp;private String date;

@OneToOne(cascade=CascadeType.ALL)@JoinColumn(name="weather_id", nullable=false)private Weather weather;

public Condition() {}

public Integer getId() { return id; }public void setId(Integer id) { this.id = id; }

// Alle weiteren getter/setter Methoden ausgelassen...}

Die Klasse Condition entspricht der Klasse weather. Sie wird mit @Entityannotiert, und hat eine gleichartige Kennzeichnung des Properties id. DieProperties text, code, temp sowie date werden alle auf Standardwerten belassen,das Property weather ist mit der Kennzeichnung @OneToOne versehen sowie einerweiteren Annotation welche das entsprechende Objekt Weather mit demFremdschlüssel des Namens weather_id in Verbindung bringt.

Multi-module Enterprise Project

124

Page 144: Maven Definitive Guide De

7.4. Das "Simple Weather" Modul - Die DiensteDas nächste Modul welches wir betrachten werden, ist so etwas wie ein "Dienst".Das "Simple Weather" Modul ist das Modul, das alle für den Abruf und dieAnalyse der Daten vom Yahoo! Wetter Dienst notwendige Logik enthält. Obwohldas "Simple Weather" Modul aus drei Java-Klassen und einem JUnit-Test besteht,stellt es eine einzige Komponente WeatherService, dar. Dieser wird sowohl durch"Simple Web" der Webapplikation, und auch "Simple Command" konsumiert.Häufig enthalten Enterprise Projekte ein oder mehrere API-Module welchekritische Business-Logik oder Logik zur Interaktion mit externen Systemenbeinhalten. Ein Banken System könnten ein Modul beinhalten, welches Dateneines externen Providers abruft und analysiert; ein System zur Anzeige vonSportergebnisse könnte einen XML-Datenstrom verarbeiten um in Echtzeit dieErgebnisse von Basketball oder Fußball darzustellen. In unserem Beispiel kapseltdieses Modul alle notwendigen Netzwerk-Aktivitäten sowie das XML-Parsing zurInteraktion mit dem Yahoo! Wetter Dienst. Andere Module können auf diesesModul aufbauen, Sie rufen einfach die Methode retrieveForecast() der KlasseWeatherService auf. Diese nimmt als Argument eine Postleitzahl und liefert einWeather-Objekt zurück.

Example 7.5. POM des simple-weather Moduls

<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.0http://maven.apache.org/maven-v4_0_0.xsd">

<modelVersion>4.0.0</modelVersion><parent>

<groupId>org.sonatype.mavenbook.ch07</groupId><artifactId>simple-parent</artifactId><version>1.0</version>

</parent><artifactId>simple-weather</artifactId><packaging>jar</packaging>

<name>Simple Weather API</name>

<dependencies><dependency><groupId>org.sonatype.mavenbook.ch07</groupId><artifactId>simple-model</artifactId>

Multi-module Enterprise Project

125

Page 145: Maven Definitive Guide De

<version>1.0</version></dependency><dependency><groupId>log4j</groupId><artifactId>log4j</artifactId><version>1.2.14</version>

</dependency><dependency><groupId>dom4j</groupId><artifactId>dom4j</artifactId><version>1.6.1</version>

</dependency><dependency><groupId>jaxen</groupId><artifactId>jaxen</artifactId><version>1.1.1</version>

</dependency><dependency><groupId>org.apache.commons</groupId><artifactId>commons-io</artifactId><version>1.3.2</version><scope>test</scope>

</dependency></dependencies>

</project>

Das "Simple Weather" POM erweitert das darüberliegende (elterliche) POM, setztdas Packaging auf jar und fügt die folgenden Abhängigkeiten ein:

org.sonatype.mavenbook.ch07:simple-model:1.0

"Simple-Weather" analysiert den Yahoo! Wetter Dienst (RSS) und verpacktdieses in ein Objekt Weather. Es verfügt über eine direkte Abhängigkeit zum"Simple Model" Modul.

log4j:log4j:1.2.14

"Simple Weather" setzt auf die Bibliothek Log4J™ zum Ausgeben vonLog-Nachrichten auf.

dom4j:dom4j:1.6.1 and jaxen:jaxen:1.1.1

Diese Beide Abhängigkeiten sind notwendig um das vom Yahoo! Wetter Dienstzurückgegebene XML zu verarbeiten.

Multi-module Enterprise Project

126

Page 146: Maven Definitive Guide De

org.apache.commons:commons-io:1.3.2 (scope=test)

Diese Abhängigkeit mit Gültigkeitsbereich test wird von der KlasseYahooParserTest benötigt.

Next is the WeatherService class, shown in Example 7.6, “Die KlasseWeatherService”. This class is going to look very similar to the WeatherService

class from Example 6.3, “Die Klasse WeatherService”. Although theWeatherService is the same, there are some subtle differences in this chapter’sexample. This version’s retrieveForecast() method returns a Weather object,and the formatting is going to be left to the applications that call WeatherService.The other major change is that the YahooRetriever and YahooParser are bothbean properties of the WeatherService bean.

Als nächstes betrachten wir die Klasse WeatherService (Example 7.6, “Die KlasseWeatherService”), diese Klasse ist sehr ähnlich zur Klasse WeatherService ausExample 6.3, “Die Klasse WeatherService” (Beispiel 6.3: Die WeatherServiceKlasse). Während die Klasse WeatherService die Gleiche ist, gibt es in diesemKapitel einige subtile Unterschiede des Beispiels zu beachten. Diese Version derMethode retrieveForecast() liefert ein Objekt Weather zurück und überlässt dieFormatierung der aufrufenden Anwendungen. Die andere große Änderung ist, dassdie Klassen YahooRetriever und YahooParser beide Bean Properties des BeansWeatherService sind.

Example 7.6. Die Klasse WeatherService

package org.sonatype.mavenbook.weather;

import java.io.InputStream;

import org.sonatype.mavenbook.weather.model.Weather;

public class WeatherService {

private YahooRetriever yahooRetriever;private YahooParser yahooParser;

public WeatherService() {}

public Weather retrieveForecast(String zip) throws Exception {// Daten holenInputStream dataIn = yahooRetriever.retrieve(zip);

Multi-module Enterprise Project

127

Page 147: Maven Definitive Guide De

// Daten auswertenWeather weather = yahooParser.parse(zip, dataIn);

return weather;}

public YahooRetriever getYahooRetriever() {return yahooRetriever;

}

public void setYahooRetriever(YahooRetriever yahooRetriever) {this.yahooRetriever = yahooRetriever;

}

public YahooParser getYahooParser() {return yahooParser;

}

public void setYahooParser(YahooParser yahooParser) {this.yahooParser = yahooParser;

}}

Schlussendlich haben wir in diesem Projekt eine XML-Datei. Diese wird vomSpring Framework benutzt, um den so genannten ApplicationContext zuerstellen. Zunächst einige Erläuterungen: unsere beiden Anwendungen, dieWeb-Anwendung und das Befehlszeilen-Dienstprogramm, müssen mit der KlasseWeatherService interagieren. Sie tun dies indem sie aus dem SpringApplicationContext eine Instanz der Klasse mit dem Namen WeatherService

holen. Unsere Web-Applikation, verwendet einen Spring MVC-Controller welchereiner Instanz des WeatherService zugeordnet ist, und unsere Dienstprogramm lädtdie Klasse WeatherService von einem Spring ApplicationContext in einerstatischen Methode main(). Um die Wiederverwendbarkeit zu fördern, haben wirunter /src/main/resources eine Datei applicationContext-weather.xmlbeigefügt, welche im Klassenpfad zur Verfügung steht. Module, welche eineAbhängigkeit zum "Simple Weather" Modul haben, können diesen Context mitdem ClasspathXmlApplicationContext des Spring Framework laden. Hieraufkönnen Sie auf eine benannte Instanz der Klasse WeatherService namensweatherService zugreifen.

Multi-module Enterprise Project

128

Page 148: Maven Definitive Guide De

Example 7.7. Spring Application Context fdes simple-weather Moduls

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans-2.0.xsddefault-lazy-init="true">

<bean id="weatherService"class="org.sonatype.mavenbook.weather.WeatherService">

<property name="yahooRetriever" ref="yahooRetriever"/><property name="yahooParser" ref="yahooParser"/>

</bean>

<bean id="yahooRetriever"class="org.sonatype.mavenbook.weather.YahooRetriever"/>

<bean id="yahooParser"class="org.sonatype.mavenbook.weather.YahooParser"/>

</beans>

Diese Konfiguration definiert drei Beans: yahooParser, yahooRetriever undweatherService. Das Bean weatherService ist eine Instanz vonWeatherService, und das XML-Dokument belegt die Properties yahooParser undyahooRetriever mit Verweisen auf benannten Instanzen der entsprechendenKlassen. Sehen Sie in der Datei applicationContext-weather.xml die Definitionder Architektur eines Teilsystems in diesem Multi-Modul-Projekt. Projekte wie"Simple Web" oder "Simple Command" können diesen Context referenzieren, undsomit eine Instanz des WeatherService abrufen welcher bereits in Bezug zuInstanzen von YahooRetriever und YahooParser steht.

7.5. Das "Simple Persist" Modul - DieDatenabstraktionDieses Modul definiert zwei sehr einfache Daten Zugriffsobjekte (Data AccessObjects: DAO). Ein DAO ist ein Objekt, das eine Schnittstelle für die Persistenzbereitstellt.

Multi-module Enterprise Project

129

Page 149: Maven Definitive Guide De

NoteWarum Persistenz und nicht Speicherung? Persistenz ist der allgemeineBegriff, während Speicherung sich hinlänglich auf ein lokales Mediumbezieht, was in keinem Fall gegeben sein muss.

In einer Anwendung welche auf Object-Relational-Mapping (ORM) Werkzeugewie Hibernate zur Abbildung aufbaut, werden in der Regel DAOs um die zupersistierenden Objekte erstellt. In diesem Projekt definieren wir zweiDAO-Objekte: WeatherDAO und LocationDAO. Die Klasse WeatherDAO erlaubt esuns, Wetter-Objekte in einer Datenbank abzuspeichern bzw. Wetter-Objekteanhand eines Schlüssels (weatherId) oder auch zu einem bestimmten Standortabzurufen. Das LocationDAO besitzt eine Methode, welche es uns erlaubt zu einergegebenen Postleitzahl das entspreche Standort-Objekt zu holen. Lassen Sie unseinen Blick auf die "Simple Persist" POM werfen:

Example 7.8. POM des "simple-persist" Moduls

<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.0http://maven.apache.org/maven-v4_0_0.xsd">

<modelVersion>4.0.0</modelVersion><parent>

<groupId>org.sonatype.mavenbook.ch07</groupId><artifactId>simple-parent</artifactId><version>1.0</version>

</parent><artifactId>simple-persist</artifactId><packaging>jar</packaging>

<name>Simple Persistence API</name>

<dependencies><dependency><groupId>org.sonatype.mavenbook.ch07</groupId><artifactId>simple-model</artifactId><version>1.0</version>

</dependency><dependency><groupId>org.hibernate</groupId><artifactId>hibernate</artifactId><version>3.2.5.ga</version>

Multi-module Enterprise Project

130

Page 150: Maven Definitive Guide De

<exclusions><exclusion>

<groupId>javax.transaction</groupId><artifactId>jta</artifactId>

</exclusion></exclusions>

</dependency><dependency><groupId>org.hibernate</groupId><artifactId>hibernate-annotations</artifactId><version>3.3.0.ga</version>

</dependency><dependency><groupId>org.hibernate</groupId><artifactId>hibernate-commons-annotations</artifactId><version>3.3.0.ga</version>

</dependency><dependency><groupId>javax.servlet</groupId><artifactId>servlet-api</artifactId><version>2.4</version><scope>provided</scope>

</dependency><dependency><groupId>org.springframework</groupId><artifactId>spring</artifactId><version>2.0.7</version>

</dependency></dependencies>

</project>

Diese POM-Datei referenziert "Simple Parent" als darüberliegendes, elterlichesPOM, sowie eine Anzahl Abhängigkeiten. Im Rahmen des "Simple Persist"Moduls sind diese:

org.sonatype.mavenbook.ch07:simple-model:1.0

Genau wie bereits im "Simple Weather" Modul, wird hier eine Abhängigkeitzum grundlegenden Objekt Modell wie es in "Simple Model" definiert ist,beschrieben.

org.hibernate:hibernate:3.2.5.ga

Wir definieren eine Abhängigkeit zu Hibernate™ Version 3.2.5.ga, aber wie Sierichtig bemerkt haben, wird eine (transitive) Abhängigkeit von Hibernateausgeschlossen. Wir tun dies, da die Abhängigkeit javax.transaction:javax

Multi-module Enterprise Project

131

Page 151: Maven Definitive Guide De

nicht im öffentlichen Maven-Repository verfügbar ist. Diese Abhängigkeit isteine dieser Abhängigkeiten zu Sun Microsystems Quellcode, welche es nochnicht in die freien zentralen Maven-Repositories geschafft hat. Um zuverhindern, dass ein immer eine ärgerlicher Meldung uns auf den notwendigenDownload dieser (unfreien) Abhängigkeit hinweist, schliessen wir diese einfachaus, und fügen eine neue Abhängigkeit hinzu ...

javax.servlet:servlet-api:2.4

Da das Projekt ein Servlet beinhaltet, müssen wir das Servlet API version 2.4einschliessen.

org.springframework:spring:2.0.7

Dies umfasst das gesamte Spring Framework als Abhängigkeit .

NoteEs ist generell besser, eine Abhängigkeit zu den Komponenten vonSpring zu definieren, zu denen diese tatsächlich besteht. Das SpringFramework-Projekt kommt Ihnen hier sehr entgegen, indem es genaudefinierende Artefakte wie z.B. spring-hibernate3 erschaffen hat.)

Warum sich von Spring abhängig machen? Bei der Integration von Hibernate,erlaubt uns der Einsatz von Spring Helfer-Klassen wie die KlasseHibernateDaoSupport auszunützen. Um Ihnen ein Beispiel dafür zu geben, wasunter Einsatz des HibernateDaoSupport möglich ist, werfen Sie einen Blick aufden Quellcode für das WeatherDAO:

Example 7.9. WeatherDAO Klasse des "simple persist" Moduls

package org.sonatype.mavenbook.weather.persist;

import java.util.ArrayList;import java.util.List;

import org.hibernate.Query;import org.hibernate.Session;import org.springframework.orm.hibernate3.HibernateCallback;import org.springframework.orm.hibernate3.support.HibernateDaoSupport;

import org.sonatype.mavenbook.weather.model.Location;

Multi-module Enterprise Project

132

Page 152: Maven Definitive Guide De

import org.sonatype.mavenbook.weather.model.Weather;

public class WeatherDAO extends HibernateDaoSupport# {

public WeatherDAO() {}

public void save(Weather weather) {#getHibernateTemplate().save( weather );

}

public Weather load(Integer id) {#return (Weather) getHibernateTemplate().load( Weather.class, id);

}

@SuppressWarnings("unchecked")public List<Weather> recentForLocation( final Location location ) {return (List<Weather>) getHibernateTemplate().execute(

new HibernateCallback() {#public Object doInHibernate(Session session) {

Query query = getSession().getNamedQuery("Weather.byLocation");query.setParameter("location", location);return new ArrayList<Weather>( query.list() );

}});

}}

Das ist alles! Nein wirklich, das ist die gesamte Klasse mit welcher Sie neue Zeileneinfügen, anhand von Primärschlüsseln Werte suchen, oder auch alle Zeilen in derWeather-Tabelle finden welche eine Referenz auf einen Ort setzen. Sie werdenverstehen, dass wir an dieser Stelle nicht einfach das Buch schliessen können umdie notwendigen fünfhundert Seiten Erklärung der Feinheiten des HibernateFrameworks einzuschieben, aber was wir tun können ist, Ihnen ein paar kurzeErklärungen geben:

❶ Die Klasse leitet sich von HibernateDaoSupport ab. Das bedeutet, dass dieKlasse mit einer Hibernate SessionFactory verbunden ist, welche zumEinsatz kommt Hibernate Session Objekte zu erstellen. In Hibernate wirdjeder Aufruf über ein Session-Objekt ausgeführt. Eine Session vermitteltZugang zu der zugrunde liegenden Datenbank und kümmert sich um dieVerwaltung der Verbindung zur JDBC-Datenquelle. Die Erweiterung vonHibernateDaoSupport bedeutet auch, dass wir Zugriff auf

Multi-module Enterprise Project

133

Page 153: Maven Definitive Guide De

HibernateTemplates mittels getHibernateTemplate() haben. Um Ihnen einBeispiel davon zu geben, was mit HibernateTemplates möglich ist ...

❷ ...die Methode save() nimmt sich einer Instanz eines Wetter Objekts an, undruft die Methode save() eines HibernateTemplate auf. DasHibernateTemplate verwandelt den Aufruf in einfache HibernateOperationen um, und wandelt jegliche Datenbank spezifische Exeptions inentsprechende Runtime-Exceptions um. In diesem Fall rufen wir save() auf,um einen Datensatz in der Datenbank, in der Tabelle Weather abzulegen.Alternativen zu diesem Aufruf wären update() sollten wir einen Eintragaktualisieren, oder saveOrUpdate(), welches den Datensatz entweder anlegtoder aktualisiert, abhängig von der Anwesenheit eines Properties id desObjekts Weather, das nicht Null sein darf.

❸ Die Methode load() ist ein weiterer Einzeiler, welcher lediglich eineMethode einer Instanz des HibernateTemplates aufruft. Die Methodeload() des HibernateTemplates hat zwei Parameter eine Klasse sowie eineserialisierbares Objekt. In diesem Fall entspricht das serialisierbare Objektdem Wert der id des Objekts Weather welches geladen werden soll.

❹ Diese letzte Methode recentForLocation() beruft sich auf ein NamedQuery

das im Wetter-Objekt-Modell festgelegt wurde. Sollten Sie sich erinnern, wirhaben im Wetter-Objekt-Modell eine Abfrage "Weather.byLocation" in derForm "from Weather w where w.location = :location" definiert. WirLaden dieses NamedQuery mit einem Verweis auf ein HibernateSession-Objekt in einem HibernateCallback der ausgeführt wird, indem dieMethode execute() eines HibernateTemplate aufgerufen wird. In dieserMethode sehen Sie, wie wir die benannten Parameter mit dem Parameter desAufrufs der Methode recentForLocation() bevölkern.

Now is a good time for some clarification. HibernateDaoSupport andHibernateTemplate are classes from the Spring Framework. They were created bythe Spring Framework to make writing Hibernate DAO objects painless. Tosupport this DAO, we’ll need to do some configuration in the simple-persist

Spring ApplicationContext definition. The XML document shown inExample 7.10, “Spring Application Context Definition für das "simple-persist"Modul” is stored in src/main/resources in a file namedapplicationContext-persist.xml.

Multi-module Enterprise Project

134

Page 154: Maven Definitive Guide De

Es ist an der Zeit für einige Klarstellungen: HibernateDaoSupport undHibernateTemplate sind Klassen des Spring Framework™. Sie wurden von denEntwicklern des Spring Framework bereitgestellt, um das Bearbeiten vonHibernate DAO-Objekte schmerzloser zu gestalten. Um dieses DAO zuunterstützen, müssen wir noch einige wenige Einstellungen in der Definition desSpring ApplicationContext von "Simple Persist" vornehmen. Das folgendeXML-Dokument ist unter /src/main/resources in einer Datei mit dem NamenapplicationContext-persist.xml abgelegt.

Example 7.10. Spring Application Context Definition für das "simple-persist"Modul

<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans-2.0.xsd"default-lazy-init="true">

<bean id="sessionFactory"class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">

<property name="annotatedClasses"><list>

<value>org.sonatype.mavenbook.weather.model.Atmosphere</value><value>org.sonatype.mavenbook.weather.model.Condition</value><value>org.sonatype.mavenbook.weather.model.Location</value><value>org.sonatype.mavenbook.weather.model.Weather</value><value>org.sonatype.mavenbook.weather.model.Wind</value>

</list></property><property name="hibernateProperties">

<props><prop key="hibernate.show_sql">false</prop><prop key="hibernate.format_sql">true</prop><prop key="hibernate.transaction.factory_class">org.hibernate.transaction.JDBCTransactionFactory

</prop><prop key="hibernate.dialect">org.hibernate.dialect.HSQLDialect

</prop><prop key="hibernate.connection.pool_size">0</prop><prop key="hibernate.connection.driver_class">org.hsqldb.jdbcDriver

</prop><prop key="hibernate.connection.url">jdbc:hsqldb:data/weather;shutdown=true

</prop><prop key="hibernate.connection.username">sa</prop>

Multi-module Enterprise Project

135

Page 155: Maven Definitive Guide De

<prop key="hibernate.connection.password"></prop><prop key="hibernate.connection.autocommit">true</prop><prop key="hibernate.jdbc.batch_size">0</prop>

</props></property>

</bean>

<bean id="locationDAO"class="org.sonatype.mavenbook.weather.persist.LocationDAO">

<property name="sessionFactory" ref="sessionFactory"/></bean>

<bean id="weatherDAO"class="org.sonatype.mavenbook.weather.persist.WeatherDAO">

<property name="sessionFactory" ref="sessionFactory"/></bean>

</beans>

Dieser ApplicationContext erreicht eine Anzahl verschiedener Dinge: zunächstwird ein SessionFactory Bean bereitgestellt, ein Bean von welchem die DAOs dieHibernate Session Objekte beziehen. Dieses Bean ist eine Instanz desAnnotatedServiceFactoryBean und wird mit einer Anzahl annotatedClassesbereitgestellt. Hier sei bemerkt, dass die Liste der bereitgestellten annotiertenKlassen die Liste der Klassen darstellt, welche in unserem Modul "Simple Model"definiert wurden. Als nächstes wird die sessionFactory entsprechend einer Reihevon Hibernate Konfigurationseigenschaften (hibernateProperties) konfiguriert. Indiesem Beispiel definieren die HibernateProperties eine Reihe von Einstellungen:

hibernate.dialect

Diese Einstellung definiert, welcher SQL-Dialekt für unsere Datenbankgeneriert wird. Da wir als Datenbank HSQLDB einsetzen, ist der gewählteDatenbank Dialekt org.hibernate.dialect.HSQLDialect. Hibernateunterstützt die SQL-Dialekte aller wichtigen Datenbank Systeme wie Oracle,MySQL, PostgreSQL und SQL Server.

hibernate.connection.*

In diesem Beispiel werden die JDBC-Verbindungsparameter aus der SpringKonfiguration heraus gesetzt. Unsere Anwendungen sind so konfiguriert, dasssie gegen eine HSQLDB unter dem Verzeichnis ./data/weather arbeiten. In

Multi-module Enterprise Project

136

Page 156: Maven Definitive Guide De

einer echten Enterprise Applikation ist es wahrscheinlicher, dass Sie dieDatenbankkonfiguration mittles JNDI externalisieren und somit von IhremQuellcode trennen.

Schließlich werden in der Definitionsdatei der Bean die beiden DAO-Objekte des"Simple Persist" Moduls erstellt und mit einer Referenz auf die eben erstelltesessionFactory Bean versehen. Genau wie im Spring Applikations- Context von"Simple Weather" definiert diese Datei applicationContext-persit.xml dieArchitektur eines Submoduls einer grösseren Anwendung. Sollten Sie mit einergrösseren Anzahl von Persistenzklassen arbeiten, bietet es sich unter Umständenan, diese in einem eigenen applicationContext ausserhalb Ihrer Anwendungzusammenzufassen.

There’s one last piece of the puzzle in simple-persist. Later in this chapter,we’re going to see how we can use the Maven Hibernate3 plugin to generate ourdatabase schema from the annotated model objects. For this to work properly, theMaven Hibernate3 plugin needs to read the JDBC connection configurationparameters, the list of annotated classes, and other Hibernate configuration from afile named hibernate.cfg.xml in src/main/resources. The purpose of this file(which duplicates some of the configuration inapplicationContext-persist.xml) is to allow us to leverage the MavenHibernate3 plugin to generate Data Definition Language (DDL) from nothing morethan our annotations. See Example 7.11, “hibernate.cfg.xml des "simple-persist"Moduls”.

Als letztes Puzzleteil des "Simple Persist" Beispiels werden wir später imKapitel erfahren, wie wir mit der Hilfe des Hibernate3 Maven Plugins aus denannotierten Modellobjekten unser Datenbankschema erzeugen können. Damit diesfunktioniert, muss das Hibernate3 Maven Plugin die Parameter derJDBC-Verbindungskonfiguration, die Liste der kommentierten Klassen und andereHibernate-Konfigurations Parameter aus einer Datei mit dem Namenhibernate.cfg.xml unter /src/main/resources auslesen können. Der Zweckdieser Datei (welche einige Konfigurationen derapplicationContext-persist.xml dupliziert) ist es, das Hibernate3 MavenPlugin so zu nutzen, dass wir aus nichts weiter als unseren annotierten Klassen diegesamte DDL erstellen können.

Multi-module Enterprise Project

137

Page 157: Maven Definitive Guide De

Example 7.11. hibernate.cfg.xml des "simple-persist" Moduls

<!DOCTYPE hibernate-configuration PUBLIC"-//Hibernate/Hibernate Configuration DTD 3.0//EN""http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">

<hibernate-configuration><session-factory>

<!-- SQL Dialekt --><property name="dialect">org.hibernate.dialect.HSQLDialect</property>

<!-- Datenbank Verbindungsparamenter --><property name="connection.driver_class">org.hsqldb.jdbcDriver</property><property name="connection.url">jdbc:hsqldb:data/weather</property><property name="connection.username">sa</property><property name="connection.password"></property><property name="connection.shutdown">true</property>

<!-- JDBC connection pool (den gestellten pool benutzen) --><property name="connection.pool_size">1</property>

<!-- Aktivierung des automatischen Hibernate Session Context Management --><property name="current_session_context_class">thread</property>

<!-- Deaktivierung des second Level Cache --><property name="cache.provider_class">org.hibernate.cache.NoCacheProvider

</property>

<!-- Ausgabe allen SQL auf stdout --><property name="show_sql">true</property>

<!-- deaktivierung der Batch funktionalität von HSQLDB um Fehler korrekt weiterzugeben --><property name="jdbc.batch_size">0</property>

<!-- Auflisten aller bestehender Property Dateien --><mapping class="org.sonatype.mavenbook.weather.model.Atmosphere"/><mapping class="org.sonatype.mavenbook.weather.model.Condition"/><mapping class="org.sonatype.mavenbook.weather.model.Location"/><mapping class="org.sonatype.mavenbook.weather.model.Weather"/><mapping class="org.sonatype.mavenbook.weather.model.Wind"/>

</session-factory></hibernate-configuration>

Die Inhalte von Example 7.10, “Spring Application Context Definition für das"simple-persist" Modul” (Beispiel 7.10: "'Simple Persist' Spring Application

Multi-module Enterprise Project

138

Page 158: Maven Definitive Guide De

Context") und Example 7.1, “Simple Parent POM Projekt” (Beispiel 7.1: "SimpleParent POM Project" ) sind redundant. Während die DateiSpringApplicationContext.xml von der Web- sowie derBefehlszeilen-Anwendung benutzt wird, findet die Datei hibernate.cfg.xml nurEinsatz zur Unterstützung des Hibernate3 Maven Plugins. Später in diesem Kapitelwerden wir sehen, wie man mittels der Datei hibernate.cfg.xml und demHibernate3 Maven Plugin ein Datenbank-Schema, basierend auf demkommentierten Objektmodell wie dieses in "Simple Model" besteht, generierenkann. Es ist die Datei hibernate.cfg.xml welche die Eigenschaften derJDBC-Verbindung konfiguriert und die annotierten Modelklassen in form einerAufzählung dem Hibernate3 Maven Plugin bereitstellt.

7.6. Das "Simple Web" Modul - DasWeb-InterfaceDie Web-Anwendung wird in einem einfachen Web Applikations Projekt definiert.Dieses einfache Web Applikations Projekt definiert zwei Spring MVC ControllerObjekte: WeatherController und HistoryController. Beide Controller werdenauf Komponenten der "Simple Weather" und "Simple Persist" Moduleabgebildet. Der Spring Container wird in der zu dieser Applikation zugehörigenDatei web.xml konfiguriert. Diese referenziert die DateiapplicationContext-weather.xml unter "Simple Weather" sowie die DateiapplicationContext-persist.xml in "Simple Persist". DieKomponenten-Architektur von dieser einfachen Web-Anwendung ist in Figure 7.3,“Abhängigkeiten des Spring MVC Controllers zu den Komponenten aus 'SimpleWeather' sowie 'Simple Persist'” (Abbildung 7.3: "Abhängigkeiten des SpringMVC Controllers zu den Komponenten aus 'Simple Weather' sowie 'SimplePersist' ") dargestellt.

Multi-module Enterprise Project

139

Page 159: Maven Definitive Guide De

Figure 7.3. Abhängigkeiten des Spring MVC Controllers zu denKomponenten aus 'Simple Weather' sowie 'Simple Persist'

Das POM des Moduls "Simple Webapp" wird unten aufgeführt:

Example 7.12. POM fder simple-webapp

<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.0http://maven.apache.org/maven-v4_0_0.xsd">

<modelVersion>4.0.0</modelVersion><parent>

<groupId>org.sonatype.mavenbook.ch07</groupId><artifactId>simple-parent</artifactId><version>1.0</version>

</parent>

<artifactId>simple-webapp</artifactId><packaging>war</packaging><name>Simple Web Application</name><dependencies>

Multi-module Enterprise Project

140

Page 160: Maven Definitive Guide De

<dependency> #<groupId>javax.servlet</groupId><artifactId>servlet-api</artifactId><version>2.4</version><scope>provided</scope>

</dependency><dependency><groupId>org.sonatype.mavenbook.ch07</groupId><artifactId>simple-weather</artifactId><version>1.0</version>

</dependency><dependency><groupId>org.sonatype.mavenbook.ch07</groupId><artifactId>simple-persist</artifactId><version>1.0</version>

</dependency><dependency><groupId>org.springframework</groupId><artifactId>spring</artifactId><version>2.0.7</version>

</dependency><dependency><groupId>org.apache.velocity</groupId><artifactId>velocity</artifactId><version>1.5</version>

</dependency></dependencies><build>

<finalName>simple-webapp</finalName><plugins><plugin> #

<groupId>org.mortbay.jetty</groupId><artifactId>maven-jetty-plugin</artifactId><dependencies>#

<dependency><groupId>hsqldb</groupId><artifactId>hsqldb</artifactId><version>1.8.0.7</version>

</dependency></dependencies>

</plugin><plugin>

<groupId>org.codehaus.mojo</groupId> #<artifactId>hibernate3-maven-plugin</artifactId><version>2.0</version><configuration>

<components><component>

<name>hbm2ddl</name><implementation>annotationconfiguration</implementation> #

</component></components>

</configuration>

Multi-module Enterprise Project

141

Page 161: Maven Definitive Guide De

<dependencies><dependency><groupId>hsqldb</groupId><artifactId>hsqldb</artifactId><version>1.8.0.7</version>

</dependency></dependencies>

</plugin></plugins>

</build></project>

Mit dem fortschreiten des Buches werden Sie festellen, dass die Datei pom.xml anLänge gewinnt. In diesem POM werden wir vier Abhängigkeiten und zwei Pluginskonfigurieren. Lassen Sie uns Schritt für Schritt das POM durchgehen und die fürdie Konfiguration wichtigen Punkte genauer erläutern:

❶ Das "Simple Web" Applikationsmodul definiert vier Abhängigkeiten: dieServlet-Spezifikation 2.4, die "Simple Weather" Dienste, die "SimplePersist" Applikations-Bibliothek sowie das gesamte Spring Framework2.0.7.

❷ Das Jetty Maven Plugin könnte nicht einfacher in dieses Projekt einzufügensein: wir fügen einfach ein Plugin-Element, welches die entsprechendenKoordinaten groupId und artifactId setzt, ein. Die Tatsache, dass diesesPlugin so trivial zu konfigurieren ist bedeutet, dass die Plugin-Entwicklereinen guten Dienst getan haben. Es wurden sinnvolle Standardwerte gesetzt,welche wir in den meisten Fällen nicht übersteuern müssen. Sollten Siedennoch die Standardeinstellungen übersteuern wollen/müssen, so tun Siedies indem Sie ein Konfigurationselement mit den entsprechenden Werteneinfügen.

❸ In unserer Build Konfiguration werden wir das Hibernate3 Maven Plugin sokonfigurieren, dass dieses gegen eine eingebettete Instanz einer HSSQLDatenbank läuft. Damit das Hibernate3 Maven Plugin auf die Datenbankzugreifen kann, muss diese einen Verweis auf die JDBC-Treiber derHSSQL-Datenbank im Klassenpfad bekommen. Um diese Abhängigkeiteines Plugins bereitzustellen, erklären wir diese direkt im Plugin-Element.Hier referenzieren wir hsqldb:hsqldb:1.8.0.7™. Das Hibernate Plugin

Multi-module Enterprise Project

142

Page 162: Maven Definitive Guide De

benötigt die JDBC-Treiber ebenfalls, um die Datenbank zu erstellen. Ausdiesem Grund haben wir diese dort ebenfalls referenziert.

❹ Es ist hier im Hibernate Maven Plugin ist, wo dieses POM anfängt interessantzu werden: Im nächsten Abschnitt werden wir das Goal hbm2ddl ausführen,um die HSQLDB Struktur zu erstellen. In dieser Datei pom.xml schliessenwir ebenfalls eine Referenz auf die Version 2.0 des Hibernate3 Maven

Plugin von Codehouse MoJo ein.❺ Das Hibernate3 Maven Plugin kann auf verschiedenerlei Wegen an die

Hibernate Mapping Informationen gelangen; abhängig vom jeweiligenEinsatzszenario des Hibernate Plugins. Sollten sie Hibernate Mapping

XML(.hbm.xml)-Dateien einsetzen und hätten vor, Modellklassen mit Hilfedes Goals hbmjava zu generieren, so würden Sie Ihre Implementierung aufconfiguration stellen. Würden Sie jedoch Hiberante3 einsetzen um einebestehende Datenbank zu re-engineeren, um daraus die entsprechenden.hbm.xml Dateien zu generieren und die Klassen dann daraus abzuleiten, sowürden Sie die Implementierung jdbcconfiguration wählen. In unserem Fallbenutzen wir einfach ein annotiertes Objekt Modell um eine Datenbank zugenerieren. In anderen Worten, wir haben bereits unser Hibernate Mapping,jedoch keine bestehende Datenbank. In diesem Fall ist wählen wirannotationconfiguration. Das Hiberante3 Maven Plugin wird unterSection 7.7, “Aufrufen der Web-Anwendung” (Abschnitt 7.7: "Aufrufen derWeb-Anwendung") näher beleuchtet.

NoteEin häufiger auftretender Fehler ist die Verwendung derextensions-Konfiguration um Abhängigkeiten von Plugins zudefinieren. Wir raten von dieser Praxis dringend ab, denn dies kann dazuführen, dass Ihr Klassenpfad -neben anderen unschönen Nebeneffekten-projektweit "verschmutzt" wird. Außerdem wird in der Version 2.1 genaudiese Funktionalität stark überarbeitet werden, was bedeutet, dass Siehier in jedem Fall noch einmal Hand anlegen müssten. Die einzige"normale" Nutzung für Extensions ist die, eine neue Paketdefinition zuerstellen.

Multi-module Enterprise Project

143

Page 163: Maven Definitive Guide De

Nun werden wir uns den beiden Spring MVC Controllern und deren Konfigurationzuwenden. Beide dieser Controller beziehen sich auf Beans welche in "SimpleWeather" bzw "Simple Persist" definiert wurden.

Example 7.13. simple-webapp WeatherController

package org.sonatype.mavenbook.web;

import org.sonatype.mavenbook.weather.model.Weather;import org.sonatype.mavenbook.weather.persist.WeatherDAO;import org.sonatype.mavenbook.weather.WeatherService;import javax.servlet.http.*;import org.springframework.web.servlet.ModelAndView;import org.springframework.web.servlet.mvc.Controller;

public class WeatherController implements Controller {

private WeatherService weatherService;private WeatherDAO weatherDAO;

public ModelAndView handleRequest(HttpServletRequest request,HttpServletResponse response) throws Exception {

String zip = request.getParameter("zip");Weather weather = weatherService.retrieveForecast(zip);weatherDAO.save(weather);return new ModelAndView("weather", "weather", weather);

}

public WeatherService getWeatherService() {return weatherService;

}

public void setWeatherService(WeatherService weatherService) {this.weatherService = weatherService;

}

public WeatherDAO getWeatherDAO() {return weatherDAO;

}

public void setWeatherDAO(WeatherDAO weatherDAO) {this.weatherDAO = weatherDAO;

}}

Die Klasse WeatherController implementiert das Spring MVC Controller Interface,welches die Existenz einer Methode handleRequest() in der oben angegebenen

Multi-module Enterprise Project

144

Page 164: Maven Definitive Guide De

Form vorschreibt. Wenn Sie das Innenleben dieser Methode betrachten, so werdenSie feststellen, dass diese die Methode retrieveForecast() der InstanzvariablenweatherService aufruft. Anders als im vorgehenden Beispiel in welchem dieKlasse WeatherService durch ein Servlet instanziert wurde, istweatherController ein Bean, mit einem weatherService Property.

Es liegt in der Verantwortung des Spring IoC Containers die Verdrahtung derKomponennte weatherService bereitzustellen. Darüber hinaus werden Siebemerkt haben, dass wir in dieser Implementierung auf den weatherFormatter

verzichten, an dessen Statt geben wir das Objekt Weather, welches vonretrieveForecast() zurückgegeben wird an den Konstruktor von ModelAndView.Die Klasse ModelAndView werden wir benutzen um Velocity Templatesdarzustellen, welche eine Referenz zu einer Variablen ${weather} halten. Daszugehörige Template weather.vm liegt unter /src/main/webapp/WEB-INF/vmabgelegt und wird in ??? (Beispiel 7.14) ausgeführt.

In the WeatherController, before we render the output of the forecast, we pass theWeather object returned by the WeatherService to the save() method onWeatherDAO. Here we are saving this Weather object—using Hibernate—to anHSQLDB database. Later, in HistoryController, we will see how we can retrievea history of weather forecasts that were saved by the WeatherController.

Innerhalb des WeatherController, geben wir das vom WeatherService

zurückgegebene Objekt Weather an die Methode save() des WeatherDAO. Dortwird das Objekt Weather mit Hilfe von Hibernate in der HSQLDB abgespeichert.Später im HistoryController werden wir sehen wie man die Vergangenheitbezüglich einer Wettervorhersage welche von HSQLDB gespeichert wurde,zurückgeholen kann.

Example 7.14. Die von WeatherController wiedergegebene Vorlageweather.vm

<b>Derzeitiges Wetter von:${weather.location.city}, ${weather.location.region},${weather.location.country}</b><br/>

<ul><li>Temperatur: ${weather.condition.temp}</li><li>Wetterlage: ${weather.condition.text}</li>

Multi-module Enterprise Project

145

Page 165: Maven Definitive Guide De

<li>Feuchtigkeit: ${weather.atmosphere.humidity}</li><li>Wind Chill Faktor: ${weather.wind.chill}</li><li>Datum: ${weather.date}</li>

</ul>

Die Syntax der Velocity Vorlagen ist einfach, Variablen werden durchvorangestelltes Dollarzeichen in geschwungenen Klammern markiert${Variable}. Der Ausdruck zwischen den geschweiften Klammern verweist aufein Property, oder ein Property eines Properties der Variable weather, welchedurch den WeatherController an das Template weitergegeben wurde.

Der HistoryController wird eingesetzt, um auf Prognosen aus der Vergangenheitzuzugreifen welche zuvor vom WeatherController angefragt wurden. Immer,wenn wir wieder eine Prognose mittels dem WeatherController abrufen, speichertder Controller das Objekt Weather mittels dem WeatherDAO in der Datenbank. DasWeatherDAO nutzt Hibernate um das Objekt Weather in eine Reihe von Zeilen zuzerlegen und in eine Anzahl in Relation stehende Datenbanktabellenabzuspeichern. Der HistoryController ist im Example 7.15, “simple-webHistoryController” (Beispiel 7.15: "Simple Web" HistoryController) ausgeführt.

Example 7.15. simple-web HistoryController

package org.sonatype.mavenbook.web;

import java.util.*;import javax.servlet.http.*;import org.springframework.web.servlet.ModelAndView;import org.springframework.web.servlet.mvc.Controller;import org.sonatype.mavenbook.weather.model.*;import org.sonatype.mavenbook.weather.persist.*;

public class HistoryController implements Controller {

private LocationDAO locationDAO;private WeatherDAO weatherDAO;

public ModelAndView handleRequest(HttpServletRequest request,HttpServletResponse response) throws Exception {

String zip = request.getParameter("zip");Location location = locationDAO.findByZip(zip);List<Weather> weathers = weatherDAO.recentForLocation( location );

Map<String,Object> model = new HashMap<String,Object>();

Multi-module Enterprise Project

146

Page 166: Maven Definitive Guide De

model.put( "location", location );model.put( "weathers", weathers );

return new ModelAndView("history", model);}

public WeatherDAO getWeatherDAO() {return weatherDAO;

}

public void setWeatherDAO(WeatherDAO weatherDAO) {this.weatherDAO = weatherDAO;

}

public LocationDAO getLocationDAO() {return locationDAO;

}

public void setLocationDAO(LocationDAO locationDAO) {this.locationDAO = locationDAO;

}}

Der HistoryController ist mit zwei in "Simple Persist" definiertenDAO-Objekten verdrahtet. Dabei sind die DAOs Bean-Properties desHistoryControllers: WeatherDAO und LocationDAO. Die Aufgabe desHistoryController ist, eine Liste von Weather Objekten entsprechend einerbestimmten Postleitzahl zurückzugeben. Wenn das WeatherDAO ein ObjektWeather in die Datenbank speichert, so legt es nicht eine Postleitzahl ab, sonder esspeichert zusätzlich eine Referenz auf ein Objekt Location welches an das ObjektWeather gebunden ist, wie in "Simple Model" definiert. Um eine Liste vonWeather-Objekte abzurufen, ruft der HistoryController zunächst das ObjektLokation auf, welches der Postleitzahl entspricht. Dies wird durch den Aufruf derMethode findByZip() des LocationDAO bewerkstelligt.

Nachdem ein Objekt Lokation abgerufen wurde, versucht der HistoryControllerdie vorgängigen Weather Objekte mit Referenz auf das Objekt Lokationabzurufen. Das Ergebnis, welches in in der Form List<Weather> besteht, wirddann in eine HashMap von zwei Variablen der Velocity Vorlage history.vm wie im??? (Beispiel 7.16: "history.vm dargestellt vom HistoryController") dargestellt,umgewandelt.

Multi-module Enterprise Project

147

Page 167: Maven Definitive Guide De

Example 7.16. history.vm dargestellt vom HistoryController

<b>Derzeitige Wetterlage von: ${location.city}, ${location.region}, ${location.country}</b><br/>

#foreach( $weather in $weathers )<ul>

<li>Temperatur: $weather.condition.temp</li><li>Zustand: $weather.condition.text</li><li>Feuchtigkeit: $weather.atmosphere.humidity</li><li>Wind Chill Faktor: $weather.wind.chill</li><li>Datum: $weather.date</li>

</ul>#end

Die Vorlage history.vm is unter /src/main/webapp/WEB-INF/vm abgelegt undverweist auf die Variable location um Angaben zum Standort/Lokation derPrognose, welche vom WeatherDAO zurückgegeben wurde auszugeben. DieseVorlage unterliegt einer Velocity Kontrollstruktur, #foreach, welche sicherstellt,dass durch alle Elemente der Variablen weathers geschlauft wird. Jedes Elementin weathers wird einer Variable weather zugeordnet und mit der Vorlage welchesich zwischen #foreach und #end befindet, verknüpft und ausgegeben.

You've seen these Controller implementations, and you've seen that theyreference other beans defined in simple-weather and simple-persist, theyrespond to HTTP requests, and they yield control to some mysterious templatingsystem that knows how to render Velocity templates. All of this magic isconfigured in a Spring application context insrc/main/webapp/WEB-INF/weather-servlet.xml. This XML configures thecontrollers and references other Spring-managed beans, it is loaded by aServletContextListener which is also configured to load theapplicationContext-weather.xml and applicationContext-persist.xml fromthe classpath. Let's take a closer look at the weather-servlet.xml shown in ???.

Sie haben nun die Controller-Implementierung gesehen, und Sie haben gesehen,dass sich deren Implementierung auf weitere Beans welche in "Simple Weather"sowie "Simple Persist" definiert sind, stützt. Sie reagieren auf HTTP-Anfragen,

Multi-module Enterprise Project

148

Page 168: Maven Definitive Guide De

und übernehmen die Kontrolle eines misteriösen Templating-Systems welchesweiß, wie man Velocity-Vorlagen verarbeitet. Diese ganze Magie wurde im SpringapplicationContext definiert, welcher sich unter/src/main/webapp/WEB-INF/weather-servlet.xml befindet. DieseXML-Konfiguration konfiguriert die Controller und definiert weitere Referenzenzu Beans welche von Spring verwaltet werden. die Datei weather-servlet.xmlwird von einem ServletContextListener geladen, welcher zugleich sokonfiguriert ist, dass er die Datei applicationContext-weather.xml sowieapplicationContext-persist.xml vom Klassenpfad lädt. Lassen Sie uns diedatei weather-servlet.xml einmal genauer anschauen.

Example 7.17. Spring Controller Konfiguration weather-servlet.xml

<beans><bean id="weatherController" #

class="org.sonatype.mavenbook.web.WeatherController"><property name="weatherService" ref="weatherService"/><property name="weatherDAO" ref="weatherDAO"/>

</bean>

<bean id="historyController"class="org.sonatype.mavenbook.web.HistoryController">

<property name="weatherDAO" ref="weatherDAO"/><property name="locationDAO" ref="locationDAO"/>

</bean>

<!-- Sie können mehr als einen Controller definieren --><bean id="urlMapping"class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">

<property name="urlMap"><map>

<entry key="/weather.x"> #<ref bean="weatherController" />

</entry><entry key="/history.x">

<ref bean="historyController" /></entry>

</map></property>

</bean>

<bean id="velocityConfig" #class="org.springframework.web.servlet.view.velocity.VelocityConfigurer">

<property name="resourceLoaderPath" value="/WEB-INF/vm/"/></bean>

Multi-module Enterprise Project

149

Page 169: Maven Definitive Guide De

<bean id="viewResolver" #class="org.springframework.web.servlet.view.velocity.VelocityViewResolver">

<property name="cache" value="true"/><property name="prefix" value=""/><property name="suffix" value=".vm"/><property name="exposeSpringMacroHelpers" value="true"/>

</bean></beans>

❶ Die Datei weather-servlet.xml definiert die beiden Controller alsSpring-verwaltete Beans. weatherController besitzt zwei Properties, welcheVerweise auf weatherService und weatherDAO darstellen;historyController verweist auf die Beans weatherDAO und locationDAO.Wenn dieser ApplicationContext erstellt wird, so geschieht dies in einemUmfeld, welches den Zugang zu den ApplicationContexten definiert,sowohl zum "Simple Persist" wie auch zum "Simple Weather" ApplicationContext. Im ??? Beispiel werden Sie sehen, wie man Spring konfigurierenkann, um mehrere Komponenten aus verschiedenen SpringKonfigurations-Dateien zusammenzuführen.

❷ Das Bean urlMapping definiert URL-Muster, welche denWeatherController und den HistoryController aufrufen. In diesemBeispiel benutzen wir SimpleUrlHandlerMapping und bilden /weather.x aufden WeatherController sowie /history.x auf den HistoryController ab.

❸ Da wir mit der Velocity-Templating-Engine arbeiten, sind wir gezwungenetliche Konfigurations-Optionen weiterzugeben. Hierzu weisen wir in derDatei velocityConfig.xml Velocity an, Vorlagen immer unter /WEB-INF/vmzu suchen.

❹ Zuletzt wird der viewResolver mit der Klasse VelocityViewResolver

konfiguriert. Es gibt bereits eine Reihe von Implementierungen desViewResolvers in Spring; diese reichen von einem Standard-ViewResolverum JSTL/JSP-Seiten zu generieren, und reicht bis zu solchen welcherFreemaker Templates erstellen können. In unserem Beispiel werden wir dieVelocity-Templating-Engine konfigurieren, sowie die Standard-Präfix undSuffixe setzen, welche automatisch an die Namen der Vorlagen angefügtwerden und an ModelAndView weitergegeben werden.

Multi-module Enterprise Project

150

Page 170: Maven Definitive Guide De

Finally, the simple-webapp project was a web.xml which provides the basicconfiguration for the web application. The web.xml file is shown in ???.

Schließlich ist das "Simple Web" Projekt eine Webanwendung, und benötigt somiteine Datei web.xml als Basiskonfiguration der Anwendung. Die zugehörige Dateiweb.xml ist ??? (7.18: "web.xml von 'Simple Web' ") wiedergegeben.

Example 7.18. web.xml von 'Simple Web'

<web-app id="simple-webapp" version="2.4"xmlns="http://java.sun.com/xml/ns/j2ee"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee

http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"><display-name>Simple Web Application</display-name>

<context-param> #<param-name>contextConfigLocation</param-name><param-value>classpath:applicationContext-weather.xmlclasspath:applicationContext-persist.xml

</param-value></context-param>

<context-param> #<param-name>log4jConfigLocation</param-name><param-value>/WEB-INF/log4j.properties</param-value>

</context-param>

<listener> #<listener-class>org.springframework.web.util.Log4jConfigListener

</listener-class></listener>

<listener><listener-class> #org.springframework.web.context.ContextLoaderListener</listener-class>

</listener>

<servlet> #<servlet-name>weather</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet

</servlet-class><load-on-startup>1</load-on-startup>

</servlet>

<servlet-mapping> #

Multi-module Enterprise Project

151

Page 171: Maven Definitive Guide De

<servlet-name>weather</servlet-name><url-pattern>*.x</url-pattern>

</servlet-mapping></web-app>

❶ Hier ein kleines Kusntstück das uns die Wiederverwendung desapplicationContext-weather.xml sowieapplicationContest.persist.xml innerhalb dieses Projekts erlaubt. DiecontextConfigLocation wird vom ContextLoaderListener zur Erstellungeines ApplicationContext benutzt. Beim Erstellen des Weather Servletswird die Datei weather-servlet.xml -wie in ??? (Beispiel 7.17: SpringController Konfiguration weather-servlet.xml) dargestellt - imZusammenhang mit dem applicationContext welcher aus dercontextConfigLocation geladen wird, ausgewertet. Auf diese Weise könnenSie in einem anderen Projekt eine Anzahl Beans definieren und auf diese überden Klassenpfad zugreifen. Da sowohl "Simple Persist" als auch "SimpleWeather" als JAR-Archiv unter /WEB-INF/lib verfügbar sein werden,müssen wir nur noch das Präfix classpath: benutzen, um diese Dateien zureferenzieren. (Eine andere Möglichkeit wäre gewesen, diese Dateien nach/WEB-INF zu kopieren, um sie dort mit einem Konstrukt wie etwa/WEB-INF/applicationContext-persist.xml zu referenzieren).

❷ Die log4jConfigLocation wird verwendet, um dem Log4JConfigListener

mitzuteilen, wo dieser die Log4J Logging-Konfiguration finden kann. Indiesem Beispiel weisen wir log4J an, unter /WEB-INF/log4j.properties zusuchen.

❸ Hiermit stellen wir sicher, dass das Log4J Subsystem konfiguriert ist, sobalddie Web-Applikation gestartet wird. Es ist wichtig, denLog4JConfigListener vor dem ContextLoaderListener zu platzieren, daSie anderenfalls unter Umständen wichtige Logging-Nachrichten verpassen,welche auf Probleme hinweisen können, die den ordungsgemässen Start derAnwendung verhindern. Insbesondere wenn Sie eine besonders große AnzahlBeans unter der Verwaltung von Spring haben, und eines der Beans beimApplikationsstart umfällt, wird Ihre Anwendung scheitern. Haben Sie dannbereits zuvor das Logging initialisiert, so haben Sei vielleicht die Möglichkeit

Multi-module Enterprise Project

152

Page 172: Maven Definitive Guide De

eine Warnung oder eine Fehlermeldung aufzufangen; andernfalls werden Siewohl keinerlei Ahnung haben warum Ihnen der Anwendungsstart misglücktist.

❹ Der ContextLoaderListener ist im Wesentlichen der Spring Container.Beim Start einer Anwendung erstellt dieser Listner einenApplicationContext aus dem Wert des contextConfigLocationParameters.

❺ Wir definieren ein Spring MVC DispatcherServlet mit dem Namenweather. Hierdurch wird Spring unter /WEB-INF/weather-servlet.xml nacheiner Spring Konfigurationsdatei suchen. Sie können so vieleDispatcherServlets erstellen, wie Sie benötigen, ein DispatcherServlet

kann eine oder mehrere Spring MVC-Controller-Implementierungenenthalten

❻ Alle in der Endung ".x" endenden Anfragen werden zum Weather Servlet

geleitet. Wir möchten hier darauf hinweisen, dass die Endung ".x" keinetiefere Bedeutung hat, es ist eine willkürliche Auswahl welche Sie mit jedembeliebigen URL-Muster ersetzen können.

7.7. Aufrufen der Web-AnwendungUm die Web-Anwendung aufrufen zu können müssen Sei zunächst mittels demHibernate3 Maven Plugin die Datenbank erstellen. Um dies zu tun, geben Sie bittedas folgende Kommando auf der Befehlszeile im Verzeichnis des "Simple Web"Projekts ein :

$ mvn hibernate3:hbm2ddl[INFO] Scanning for projects...[INFO] Searching repository for plugin with prefix: 'hibernate3'.[INFO] org.codehaus.mojo: checking for updates from central[INFO] ------------------------------------------------------------------------[INFO] Building Chapter 7 Simple Web Application[INFO] task-segment: [hibernate3:hbm2ddl][INFO] ------------------------------------------------------------------------[INFO] Preparing hibernate3:hbm2ddl...10:24:56,151 INFO org.hibernate.tool.hbm2ddl.SchemaExport - export complete[INFO] ------------------------------------------------------------------------[INFO] BUILD SUCCESSFUL[INFO] ------------------------------------------------------------------------

Multi-module Enterprise Project

153

Page 173: Maven Definitive Guide De

Nach Abschluss der Ausführung sollte ein Verzeichnis ${basedir}/data erstelltsein welches die HSQLDB Datenbank enthält. Um die Web Applikation nun zustarten geben Sie folgenden Befehl ein:

$ mvn jetty:run[INFO] Scanning for projects...[INFO] Searching repository for plugin with prefix: 'jetty'.[INFO] ------------------------------------------------------------------------[INFO] Building Chapter 7 Simple Web Application[INFO] task-segment: [jetty:run][INFO] ------------------------------------------------------------------------[INFO] Preparing jetty:run...[INFO] [jetty:run][INFO] Configuring Jetty for project: Chapter 7 Simple Web Application...[INFO] Context path = /simple-webapp[INFO] Tmp directory = determined at runtime[INFO] Web defaults = org/mortbay/jetty/webapp/webdefault.xml[INFO] Web overrides = none[INFO] Starting jetty 6.1.7 ...2008-03-25 10:28:03.639::INFO: jetty-6.1.7...2147 INFO DispatcherServlet - FrameworkServlet 'weather': \

initialization completed in 1654 ms2008-03-25 10:28:06.341::INFO: Started [email protected]:8080[INFO] Started Jetty Server

Nach dem Start von Jetty, können Sie mit dem Aufrufhttp://localhost:8080/simple-webapp/weather.x?zip=60202 das Wetter vonEvanston, IL aufrufen. Mit der Änderung der Postleitzahl sollte es Ihnen möglichsein Ihren eigenen Wetterbericht abzurufen.

Derzeitige Wetterlage von: Evanston, IL, US

* Temperatur: 42* Zustand: Partly Cloudy* Feuchtigkeit: 55* Wind Chill Faktor: 34* Datum: Tue Mar 25 10:29:45 CDT 2008

7.8. Das "Simple Command" Modul - DasKommandozeilen Modul

Multi-module Enterprise Project

154

Page 174: Maven Definitive Guide De

Das "Simple Command" Modul ist die Befehlszeilen-Version der "Simple Web"Anwendung. Es ist ein Dienstprogramm, das auf den selben Abhängigkeitenaufbaut: "Simple Weather" und "Simple Persist". Statt der Interaktion übereinen Web-Browser, rufen Sie für diese Anwendung ein Dienstprogramm'simple-command' von der Befehlszeile aus auf.

Figure 7.4. Dienstprogramm aufbauend auf die Modue Simple-Weather undSimple-Persist

Example 7.19. POM des Simple-Command Moduls

<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.0http://maven.apache.org/maven-v4_0_0.xsd">

<modelVersion>4.0.0</modelVersion><parent>

<groupId>org.sonatype.mavenbook.ch07</groupId><artifactId>simple-parent</artifactId><version>1.0</version>

Multi-module Enterprise Project

155

Page 175: Maven Definitive Guide De

</parent>

<artifactId>simple-command</artifactId><packaging>jar</packaging><name>Simple Command Line Tool</name>

<build><finalName>${project.artifactId}</finalName><plugins><plugin>

<groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><configuration>

<source>1.5</source><target>1.5</target>

</configuration></plugin><plugin>

<groupId>org.apache.maven.plugins</groupId><artifactId>maven-surefire-plugin</artifactId><configuration>

<testFailureIgnore>true</testFailureIgnore></configuration>

</plugin><plugin><artifactId>maven-assembly-plugin</artifactId><configuration>

<descriptorRefs><descriptorRef>jar-with-dependencies</descriptorRef>

</descriptorRefs></configuration>

</plugin><plugin>

<groupId>org.codehaus.mojo</groupId><artifactId>hibernate3-maven-plugin</artifactId><version>2.1</version><configuration>

<components><component>

<name>hbm2ddl</name><implementation>annotationconfiguration</implementation>

</component></components>

</configuration><dependencies>

<dependency><groupId>hsqldb</groupId><artifactId>hsqldb</artifactId><version>1.8.0.7</version>

</dependency></dependencies>

</plugin></plugins>

Multi-module Enterprise Project

156

Page 176: Maven Definitive Guide De

</build>

<dependencies><dependency><groupId>org.sonatype.mavenbook.ch07</groupId><artifactId>simple-weather</artifactId><version>1.0</version>

</dependency><dependency><groupId>org.sonatype.mavenbook.ch07</groupId><artifactId>simple-persist</artifactId><version>1.0</version>

</dependency><dependency><groupId>org.springframework</groupId><artifactId>spring</artifactId><version>2.0.7</version>

</dependency><dependency><groupId>hsqldb</groupId><artifactId>hsqldb</artifactId><version>1.8.0.7</version>

</dependency></dependencies>

</project>

Dieses POM wird die Erstellung eines JAR files veranlassen, welches die Klasseorg.sonatype.mavenbook.weather.Main beinhaltet. Diese Klasse wird inExample 7.20, “Klasse Main des Simple-Command Moduls” (Beispiel7.20:"Klasse Main des Simple-Command Moduls" dargestellt. In dieserpom.xml-Datei werden wir das Maven Plugin Assembly um einen bereitsmitgelieferten "Assembly Beschrieb" mit dem Namen "jar-with-dependencies"konfigurieren. Dieses Plugin wird sodann ein JAR- Archive erstellen, welchesallen Bytecode beinhaltet welcher gebraucht wird, um das Projekt auszuführen,wie auch alle weitergehenden Abhängigkeiten.

Example 7.20. Klasse Main des Simple-Command Moduls

package org.sonatype.mavenbook.weather;

import java.util.List;

import org.apache.log4j.PropertyConfigurator;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;

Multi-module Enterprise Project

157

Page 177: Maven Definitive Guide De

import org.sonatype.mavenbook.weather.model.Location;import org.sonatype.mavenbook.weather.model.Weather;import org.sonatype.mavenbook.weather.persist.LocationDAO;import org.sonatype.mavenbook.weather.persist.WeatherDAO;

public class Main {

private WeatherService weatherService;private WeatherDAO weatherDAO;private LocationDAO locationDAO;

public static void main(String[] args) throws Exception {// Configure Log4JPropertyConfigurator.configure(Main.class.getClassLoader().getResource(

"log4j.properties"));

// Read the Zip Code from the Command-line (if none supplied, use 60202)String zipcode = "60202";try {zipcode = args[0];

} catch (Exception e) {}

// Read the Operation from the Command-line (if none supplied use weather)String operation = "weather";try {operation = args[1];

} catch (Exception e) {}

// Start the programMain main = new Main(zipcode);

ApplicationContext context =new ClassPathXmlApplicationContext(

new String[] { "classpath:applicationContext-weather.xml","classpath:applicationContext-persist.xml" });

main.weatherService = (WeatherService) context.getBean("weatherService");main.locationDAO = (LocationDAO) context.getBean("locationDAO");main.weatherDAO = (WeatherDAO) context.getBean("weatherDAO");if( operation.equals("weather")) {main.getWeather();

} else {main.getHistory();

}}

private String zip;

public Main(String zip) {this.zip = zip;

}

Multi-module Enterprise Project

158

Page 178: Maven Definitive Guide De

public void getWeather() throws Exception {Weather weather = weatherService.retrieveForecast(zip);weatherDAO.save( weather );System.out.print(new WeatherFormatter().formatWeather(weather));

}

public void getHistory() throws Exception {Location location = locationDAO.findByZip(zip);List<Weather> weathers = weatherDAO.recentForLocation(location);System.out.print(new WeatherFormatter().formatHistory(location, weathers));

}}

Die Klasse Main hält eine Referenz auf die Klassen WeatherDAO, LocationDAO undWeatherService. Folgende Aufgaben werden durch deren statische Methodemain() wahrgenommen:

• Die Postleitzahl aus dem ersten Argument ([0]) der Befehlszeile auslesen

• Das Kommando aus dem zweiten Argument ([1]) der Befehlszeile auslesen.Ist das Kommando "weather", so werden die aktuellen Wetterdaten vomWebservice angefragt. Ist das Kommando "history" so werden dieWetterdaten aus der lokalen Datenbank abgerufen.

• Laden des Spring ApplicationContext unter Bezug auf zwei XML Dateien,welche von den Modulen "Simple Persist" und "Simple Weather" geladenwurden.

• Instanzieren der Klasse Main

• Befruchten von WeatherService, WeatherDAO sowie LocationDAO mit Beansaus dem Spring ApplicationContext

• Aufrufen der entsprechenden Methode getWeather() oder getHistory()entsprechend dem übergebenen Kommando

Als Teil der Web Applikation benutzen wir Spring VelocityViewResolver umeine Velocity Vorlage darzustellen. Für die befehlszeilenorientierte Applikation istes notwendig eine einfache Klasse zu erstellen, welche unsere Wetterdaten mit

Multi-module Enterprise Project

159

Page 179: Maven Definitive Guide De

Hilfe einer Velocity Vorlage darstellt. Example 7.21, “WeatherFormatter stelltWetterdaten unter Einbezug einer Velocity Vorlage dar” (Beispiel 7.21:"WeatherFormatter stellt Wetterdaten unter Einbezug einer Velocity Vorlagedar") ist der Auszug der Klasse WeatherFormatter, einer Klasse mit zweiMethoden welche den Wetterbericht und die Wettergeschichte darstellen.

Example 7.21. WeatherFormatter stellt Wetterdaten unter Einbezug einerVelocity Vorlage dar

package org.sonatype.mavenbook.weather;

import java.io.InputStreamReader;import java.io.Reader;import java.io.StringWriter;import java.util.List;

import org.apache.log4j.Logger;import org.apache.velocity.VelocityContext;import org.apache.velocity.app.Velocity;

import org.sonatype.mavenbook.weather.model.Location;import org.sonatype.mavenbook.weather.model.Weather;

public class WeatherFormatter {

private static Logger log = Logger.getLogger(WeatherFormatter.class);

public String formatWeather( Weather weather ) throws Exception {log.info( "Formatting Weather Data" );Reader reader =new InputStreamReader( getClass().getClassLoader().

getResourceAsStream("weather.vm"));VelocityContext context = new VelocityContext();context.put("weather", weather );StringWriter writer = new StringWriter();Velocity.evaluate(context, writer, "", reader);return writer.toString();

}

public String formatHistory( Location location, List<Weather> weathers )throws Exception {

log.info( "Formatting History Data" );Reader reader =new InputStreamReader( getClass().getClassLoader().

getResourceAsStream("history.vm"));VelocityContext context = new VelocityContext();context.put("location", location );context.put("weathers", weathers );StringWriter writer = new StringWriter();

Multi-module Enterprise Project

160

Page 180: Maven Definitive Guide De

Velocity.evaluate(context, writer, "", reader);return writer.toString();

}}

Die Vorlage weather.vm gibt die zugehörige Stadt, den Staat, die Region sowie dieaktuelle Temperatur aus. Die Vorlage history.vm gibt die Örtlichkeit aus, unditeriert dann über alle gespeicherten Wetterwerte der lokalen Datenbank. Diesebeiden Vorlagen befinden sich unter ${basedir}/src/main/resource.

Example 7.22. Die velocity Vorlage weather.vm

****************************************Current Weather Conditions for:

${weather.location.city},${weather.location.region},${weather.location.country}

****************************************

* Temperature: ${weather.condition.temp}* Condition: ${weather.condition.text}* Humidity: ${weather.atmosphere.humidity}* Wind Chill: ${weather.wind.chill}* Date: ${weather.date}

Example 7.23. Die Velocity Vorlage history.vm

Weather History for:${location.city},${location.region},${location.country}

#foreach( $weather in $weathers )***************************************** Temperature: $weather.condition.temp* Condition: $weather.condition.text* Humidity: $weather.atmosphere.humidity* Wind Chill: $weather.wind.chill* Date: $weather.date#end

Multi-module Enterprise Project

161

Page 181: Maven Definitive Guide De

7.9. Aufrufen derKommandozeilen-AnwendungDas Projekt Modul simple-command ist konfiguriert, ein einziges JAR Archiv,welches den Bytecode des Projektes sowie aller Bytecode der Abhängigkeitenenthält, zu erstellen. Um diese Baueinheit (Assembly) zu erzeugen, rufen Sie dasGoal assembly des Maven Assembly Plugin von innerhalb des Verzeichnisses desProjektes "Simple Command" auf:

$ mvn assembly:assembly[INFO] ------------------------------------------------------------------------[INFO] Building Chapter 7 Simple Command Line Tool[INFO] task-segment: [assembly:assembly] (aggregator-style)[INFO] ------------------------------------------------------------------------[INFO] [resources:resources][INFO] Using default encoding to copy filtered resources.[INFO] [compiler:compile][INFO] Nothing to compile - all classes are up to date[INFO] [resources:testResources][INFO] Using default encoding to copy filtered resources.[INFO] [compiler:testCompile][INFO] Nothing to compile - all classes are up to date[INFO] [surefire:test]...[INFO] [jar:jar][INFO] Building jar: .../simple-parent/simple-command/target/simple-command.jar[INFO] [assembly:assembly][INFO] Processing DependencySet (output=)[INFO] Expanding: .../.m2/repository/.../simple-weather-1-SNAPSHOT.jar into \

/tmp/archived-file-set.93251505.tmp[INFO] Expanding: .../.m2/repository/.../simple-model-1-SNAPSHOT.jar into \

/tmp/archived-file-set.2012480870.tmp[INFO] Expanding: .../.m2/repository/../hibernate-3.2.5.ga.jar into \

/tmp/archived-file-set.1296516202.tmp... skipping 25 lines of dependency unpacking ...[INFO] Expanding: .../.m2/repository/.../velocity-1.5.jar into /tmp/archived-file-set.379482226.tmp[INFO] Expanding: .../.m2/repository/.../commons-lang-2.1.jar into \

/tmp/archived-file-set.1329200163.tmp[INFO] Expanding: .../.m2/repository/.../oro-2.0.8.jar into /tmp/archived-file-set.1993155327.tmp[INFO] Building jar: .../simple-parent/simple-command/target/simple-command-jar-with-dependencies.jar

Der Build schreitet durch die Lebenszyklusphasen Kompilieren des Bytecodes,Aufrufen der Tests und schliesslich Erstellen des JAR-Archives. Zuletzt erstellt dasGoal assembly:assembly ein einziges JAR Archiv inklusive aller Abhängigkeiten,indem es allen abhängigen Bytecode in temporäre Verzeichnisse entpackt um

Multi-module Enterprise Project

162

Page 182: Maven Definitive Guide De

diesen schliesslich in einem einzigen JAR Archiv zusammenzufassen und imZielverzeichnis /target unter dem Namensimple-command-jar-with-dependencies.jar abzulegen. Dieses "über" JARArchiv kommt auf stolze 15 MB.

Vor Sie nun das kommandozeilenorientierte Programm aufrufen, müssen Sie nochdas Goal hbm2ddl des Maven Hibernate3 Plugin aufrufen, um die HSQLDatenbank zu erstellen. Sie tun dies, indem Sie den folgenden Befehl innerhalb desVerzeichnisses von "Simple Command" absetzen:

$ mvn hibernate3:hbm2ddl[INFO] Scanning for projects...[INFO] Searching repository for plugin with prefix: 'hibernate3'.[INFO] org.codehaus.mojo: checking for updates from central[INFO] ------------------------------------------------------------------------[INFO] Building Chapter 7 Simple Command Line Tool[INFO] task-segment: [hibernate3:hbm2ddl][INFO] ------------------------------------------------------------------------[INFO] Preparing hibernate3:hbm2ddl...10:24:56,151 INFO org.hibernate.tool.hbm2ddl.SchemaExport - export complete[INFO] ------------------------------------------------------------------------[INFO] BUILD SUCCESSFUL[INFO] ------------------------------------------------------------------------

Nach dem Aufruf sollten Sie innerhalb des "Simple Command" Verzeichnisses einVerzeichnis /data vorfinden. Dieses Verzeichnis beinhaltet die HSQL Datenbank.Um nun das befehlszeilenorientierte Programm aufzurufen, setzen Sie denfolgenden Befehl von innerhalb des "Simple Command" Verzeichnisses ab:

$ java -cp target/simple-command-jar-with-dependencies.jar \org.sonatype.mavenbook.weather.Main 60202

2321 INFO YahooRetriever - Retrieving Weather Data2489 INFO YahooParser - Creating XML Reader2581 INFO YahooParser - Parsing XML Response2875 INFO WeatherFormatter - Formatting Weather Data****************************************Current Weather Conditions for:

Evanston,IL,US

****************************************

* Temperature: 75* Condition: Partly Cloudy* Humidity: 64* Wind Chill: 75

Multi-module Enterprise Project

163

Page 183: Maven Definitive Guide De

* Date: Wed Aug 06 09:35:30 CDT 2008

Um eine Abfrage der vergangenen Werte abzusetzen, geben Sie den folgendenBefehl ein:

$ java -cp target/simple-command-jar-with-dependencies.jar \org.sonatype.mavenbook.weather.Main 60202 history

2470 INFO WeatherFormatter - Formatting History DataWeather History for:Evanston, IL, US

***************************************** Temperature: 39* Condition: Heavy Rain* Humidity: 93* Wind Chill: 36* Date: 2007-12-02 13:45:27.187***************************************** Temperature: 75* Condition: Partly Cloudy* Humidity: 64* Wind Chill: 75* Date: 2008-08-06 09:24:11.725***************************************** Temperature: 75* Condition: Partly Cloudy* Humidity: 64* Wind Chill: 75* Date: 2008-08-06 09:27:28.475

7.10. FazitNun, wir haben viel Zeit damit verbracht, uns Themen anzunehmen, welche nichtim Zusammenhang mit Maven stehen. Wir haben dies getan, um ein vollständigesund aussagekräftiges Beispielprojekt mit Bezug zur realen Welt zuimplementieren. Wir haben uns keine schnellen Abkürzungen geleistet, um Ihnenein hochglanzpoliertes fix fertiges Produkt zu liefern. Auch war unser Ziel nicht,Sie mit einer Ruby on Rails artigen Zauberei zur Annahme zu führen, dass Sie nunin wenigen Minuten eine fix-fertige Java-Enterprise-Anwendung hinstellenkönnten. - Hiervon gibt es bereits zu viele Beispiel im Markt, zu viele Menschenwelche Ihnen das einfachste, umfassenste Framework zu verkaufen versuchen,welches Ihnen erlaubt mit Null Investition von Zeit und Aufmerksamkeit

Multi-module Enterprise Project

164

Page 184: Maven Definitive Guide De

weiterzukommen. Unser Ziel diese Kapitels war es, Ihnen ein umfassendes Bild zuvermitteln: das gesamte Ökosystem eines Multi-Modul Projekt Builds. Was wirbieten ist Maven im Rahmen einer Anwendung einer Art ähnlich deren, welche Siein freier Wildbahn antreffen, darzustellen. Es ist nicht unsere Art Ihnen eine ArtFast-Food anzubieten: 10 Minuten Bildschirm Show, etwas Schlamm in dieRichtung Apache Ant geworfen und Sie davon überzeugt haben Maveneinzusetzen!

Sollten Sie sich nach dem Lesen dieses Kapitels fragen, was es mit Maven zu tunhat, so waren wir erfolgreich. Wir haben eine Reihe von komplex vernetztenProjekten, unter Einsatz von populären Frameworks erzeugt, und wir haben dieseunter Verwendung von deklarativen Methoden verbunden. Die Tatsache, dassmehr als 60% dieses Kapitel der Erklärung von Spring und Hibernate gewidmentwurde, sollte Ihnen aufzeigen, dass Maven grösstenteils zurücksteht. Esfunktionierte einfach. Es erlaubt uns, uns auf die Anwendung selbst zukonzentrieren, und nicht so sehr auf der Build-Prozess. Sie werden bemerken, dasshaben wir ein Projekt erstellt haben, welches Daten in einer Datenbank speichert,ohne ein einziges SQL-Statement abzusetzen. Daraufhin haben Sie eineWeb-Anwendung erstellt, ohne sich mit den Erstellung eines Build Skriptesinklusive 20 plus Abhängigkeiten herumzuschlagen um am Ende ein WAR-Archivzu erstellen. Anstelle Zeit damit zu verbringen Maven zu diskutieren, verbrachtenwir die Zeit damit Spring und Hiberante in den Grundzügen einzuführen.

Sie können das eingeführte Skelett Projekt dieses Kapitels als Grundlage für Ihreeigene Projekte verwenden, und die Chancen stehen gut, dass, sollten Sie das tun,Sie sich in der Lage befinden über die Zeit hinweg mehr und mehr Module nachBedarf anzulegen. So umfasst das ursprüngliche Projekt, auf welchem diesesBeispiel beruht zwei verschiedene Modell-Projekte, mit zwei Persistenzprojektenwelche den Anschuss an völlig unterschiedliche Datenbanksysteme übernehmen,mehrere Web-Applikationen, sowie eine Java-Mobil Anwendung. Insgesamt stütztsich das reale Welt-System auf mindestens 15 miteinander verknüpften Module.Was wir hiermit sagen wollen ist, Sie haben soeben das komplexeste Beispielwelches in diesem Buch dargestellt wird gesehen, aber Sie sollten auch wissen,dass dieses Beispiel nur Kratzer an der Oberfläche dessen was mit Maven möglichist, darstellt.

Multi-module Enterprise Project

165

Page 185: Maven Definitive Guide De

7.10.1. Programmierung gegen Interface-ProjekteDieses Kapitel stieg in die Tiefe der Multi-Modul-Projekte hinab und warkomplexer als die einfachen Beispiele in Chapter 6, Ein multi-modulares Projekt(Kapitel 6: Ein Multi-Projekt-Modul), und dennoch war es eine starkeVereinfachung eines Reale- Welt-Projektes. In einem größeren Projekt finden Sievielleicht sogar selbst ein System ähnlich Figure 7.5, “Programmierung gegenInterface-Projekte” (Abbildung 7.5) wieder.

Figure 7.5. Programmierung gegen Interface-Projekte

Wenn wir den Begriff Interface Projekt benutzen, so beziehen wir uns auf einMaven Projekt, welches einzig aus Interfaces und Konstanten besteht. InFigure 7.5, “Programmierung gegen Interface-Projekte” (Abbildung 7.5Programmierung gegen ein Interface Projekt), stellen die Einheiten persist-api

Multi-module Enterprise Project

166

Page 186: Maven Definitive Guide De

sowie parse-api Interfaceprojekte dar. Sollten big-command sowie big-webapp

gegen diese Interface Projekte programmiert werden, ist es ein leichtes zu einemspäteren Zeitpunkt die Implementierung der Persistenz gegen eine andereauszutauschen. Auf dieser Abbildung werden zwei Implementierungen dargestellt:eine welche die Persistenz aufbauend auf einen XML-Store implementiert, sowieeiner weiteren auf der Basis einer relationalen Datenbank. Bei der Anwendung derin diesem Kapitel vorgestellten Konzepte ist es einfach zu sehen, wie Sie,getrieben durch einen an das Programm übergebenen Wert, eine andere SpringApplicationContext XML Datei übergeben, um damit die unterliegendePersistenzimplementierung auszutauschen. Wie bereits im OO-Design derApplikation, ist oftmals sinnvoll das Interface einer API von der Implementierungeiner API in seperate Maven Projekte aufzutrennen.

Multi-module Enterprise Project

167

Page 187: Maven Definitive Guide De

Chapter 8. Optimirung und Überarbeitungder POMs

8.1. EinführungIn Chapter 7, Multi-module Enterprise Project (Kapitel 7: "Multi-Modul EnterpriseProjekt"), zeigten wir auf, wie die verschiedenen Teile eines Maven Buildszusammenkommen, um eine voll funktionsfähige multi modularre Anwendung zuerstellen. Während das Beispiel des Kapitels eine wirklichkeitsnahe Anwendungwiederspiegelt - eine Anwendung mit Datenbankintegration, einem Web-Service,und sogar zwei Benutzer Schnittstellen: eine in Form einer Web-Anwendung, undeine als Dienstprogramm; so bleibt das Beispiel-Projekt des Kapitels letztendlichein konstruiertes. Um die Komplexität eines realen Projektes abzubilden bräuchtees ein Buch von weit grösserem Umfang als das, das Sie gerade lesen.Anwendungen des täglichen Lebens entwickeln sich im Laufe der Jahre undwerden oftmals von großen, unterschiedlichen Gruppen von Entwicklern gewartet,welche jeweils ganz verschiedene Schwerpunkte setzen. In einem solchen Projekt,müssen Sie häufig Entscheidungen und Designs gegeneinander abwägen, welchejemand anderes getätigt hat. In diesem Kapitel treten wir nun einen Schritt zurückvon den Beispielen, aus Part I, “Maven by Example” (Teil I: Maven by Example),und stellen uns die Frage, ob es irgendwelche Optimierungen gibt, welche jetzt, wowir mehr über Maven wissen, Sinn machen. Maven ist ein vielfältiges Werkzeug,welches so einfach oder auch komplex sein kann, wie Sie es gerade benötigen. Ausdiesem Grund gibt es oft viele verschiedene Möglichkeiten, um die gleicheAufgabe zu bewerkstelligen, und oft gibt es keinen einzigen "richtigen" Weg beider Konfiguration Ihres Maven-Projektes.

Bitte verstehen Sie den letzten Satz nicht falsch. Versuchen Sie nun nicht mitMaven etwas zu lösen, für das Maven nicht konzipiert wurde! Auch wenn Maveneine Vielfalt verschiedener Ansätze unterstützt, gibt es natürlich den "Maven Way",und Sie werden mit Maven produktiver sein wenn Sie diesen einschlagen. Das Zieldiesen Kapitels ist es, einige der Optimierungen weiterzugeben, welche Sie auch

168

Page 188: Maven Definitive Guide De

auf ein bestehendes Maven-Projekt anwenden können. Warum haben wir nichtgleich ein optimiertes POM eingeführt, warum der Umweg? POMs pädagogischsinnvoll zu gestalten, und POMs in der Praxis effizient zu gestalten, sind zwei sehrunterschiedliche Anforderungen. Zwar ist es sicherlich sehr viel einfacher, einebestimmte Einstellung in Ihrem Profil in der ~/.m2/settings.xml zu definieren,als deren Einbindung in der pom.xml Datei, aber wenn man ein Buch schreibtdamit dieses gelesen wird, geht es auch meist darum das Tempo richtig zu wählenund dafür zu sorgen, dass nicht Konzepte eingeführt werden, bevor Sie dazu bereitsind. In Part I, “Maven by Example” (Teil I: Maven by Example), haben wir unsgrosse Mühe gegeben, den Leser nicht mit zu viel Information zu überlasten, undhaben darum einige grundlegende Konzepte wie das des ElementsdependencyManagement bewusst übersprungen. Dieses Element wird nun indiesem Kapitel eingeführt.

Im Part I, “Maven by Example” (Teil I: "Maven by Example") dieses Buches gibtes Beispiele, in welchen die Autoren eine Abkürzung gewählt haben, oder über einwichtiges Detail grosszügig hinweggeschritten sind, einfach um Sie zügig zu denKernpunkten des Kapitels weiterzuleiten. Sie konnten lernen wie man ein MavenProjekt aufsetzt, kompiliert und installiert, ohne dass Sie durch hunderte SeitenDetailspezifikation waten mussten, welche jeden letzten Schalter erklärte, derIhnen zur Verfügung gestanden hätte. Wir haben diesen Weg bewusst gewählt, daes uns wichtig schien, den neuen Maven Benutzer schnell zu einem Ergebnis zuführen, statt Ihn auf einem langen mäandernden Weg zu begleiten, welcher unsdurch eine schier endlos erscheinende Weite führt. Sobald Sie damit begonnenhaben, selbst regelmässig Maven einzusetzen, sollten Sie wissen, wie man eigeneProjekte und POMs analysiert. In diesem Kapitel treten wir nun einen Schrittzurück, und werfen einen Blick auf das was wir zum Abschlus von Chapter 7,Multi-module Enterprise Project (Kapitel 7: "Multi-Modul Enterprise Projekt")tatsächlich bekommen haben.

8.2. POM BereinigungDie Optimierung eines multi-modularen POMs wird am besten in mehreren Phasendurchgeführt, denn es gilt sich auf unterschiedliche Bereiche zu konzentrieren. Im

Optimirung und Überarbeitung der POMs

169

Page 189: Maven Definitive Guide De

Normalfall suchen wir nach Wiederholungen innerhalb eines POM und überdessen Geschwister-POMs hinweg. Wenn Sie am Anfang stehen, oder wenn sichein Projekt noch immer sehr schnell entwickelt, es ist akzeptabel, einigeAbhängigkeiten und Plugin-Konfigurationen hier und da zu duplizieren, aber sowie das Projekt reift und die Anzahl der Module sich erhöht, sollten Sie sich Zeitnehmen um die gemeinsamen Abhängigkeiten sowie Plugin Konfigurationen zurefaktorieren/überarbeiten. Die Erstellung effizienter POMs trägt Sie ein grossesStück des Weges, die Komplexität Ihres Projekts zu verwalten während diesesnoch immer wächst. Wo immer Verdopplungen von Informationen auftauchenkönnen Sie davon ausgehen, dass es in der Regel einen besseren Weg gibt.

8.3. Optimirung der AbhängigkeitenWenn Sie einen Blick in die verschiedenen POMs in Chapter 7, Multi-moduleEnterprise Project (Kapitel 7: "Multi-Modul Enterprise Projekt") werfen, beachtenSie mehrere bestehende Muster der Replikation/Wiederholung. Das erste Muster,das wir sehen können ist, dass einige Abhängigkeiten wie Spring undHibernate-Annotations in mehreren Modulen definiert werden. Die Hibernate

Abhängigkeit hat in jeder Definition die Ausgrenzung von javax.transactionrepliziert. Das zweite Muster von Wiederholung ist, dass manchmal mehrereAbhängigkeiten in einem Zusammenhang stehen und sich die gleiche Versionteilen. Dies ist häufig der Fall, wenn ein Projekt Release aus mehreren enggekoppelten Komponenten besteht. Sehen Sie sich zum Beispiel dieAbhängigkeiten der Hibernate-Annotations sowie derHibernate-Commons-Annotations an: beide tragen als Version 3.3.0.ga, und wirkönnen davon ausgehen, dass die Version der beiden Abhängigkeiten sich auch inZukunft im Gleichschritt bewegen wird. Sowohl die Hibernate-Annotations wieauch Hibernate-Commons-Annotations sind Komponenten des gleichen Projekts,freigegeben von JBoss. Wann immer es einen neuen Projekt Release gibt, werdendiese beiden Abhängigkeiten sich ändern. Das letzte Muster der Replikation ist dieWiederholung der Geschwister-Modul Abhängigkeiten undGeschwister-Modul-Versionen. Maven bietet einfache Mechanismen, mit denenSie alle Fälle von Duplikation in das Top Level POM (Parent POM) ziehen

Optimirung und Überarbeitung der POMs

170

Page 190: Maven Definitive Guide De

können.

Ein kleine Diskrepanz der Version einer Abhängigkeit eines Projektes bezüglicheines Bytecode-Manipulations Bibliothek mit dem Namen ASM, drei Ebenentiefer in der Projekt-Hierarchie, führt leicht zur vollständigen Blockade einerWeb-Applikation, welche von einer ganz anderen Gruppe von Entwicklernbearbeitet wird, und von diesem speziellen Modul abhängig ist. Unit-Testsbestehen, da diese gegen eine Version der Abhängigkeit testen, und scheiternfürchterlich in der Produktion, in welcher - in einem WAR-Archive gebündelt -eine ganz andere Version dieser Abhängigkeit vorliegt. Sollten Sie Dutzende vonProjekten am Laufen haben, welche alle mit etwas wie Hibernate Annotations

arbeiten und deren Deklaration sich ständig wiederholt und dupliziert, mit allenAbhängigkeiten und Ausschlüssen, so wird die durchschnittliche Zeit zwischengrossen Build Fehlern eher klein sein. So wie Ihre Maven Projekte immer weiterwachsen und komplexer werden, werden Ihre Listen von Abhängigkeiten wachsen;und Sie werden nicht umhinkommen, Versionen und Deklarationen IhrerAbhängigkeiten in Ihrem Top Level POM zu konsolidieren.

Die Wiederholung von Geschwister-Modul-Versionen bereiten das Feld für einenbesonders fieses Problem, welches zwar nicht direkt durch Maven verschuldetwird, aber dessen man sich erst bewusst wird, nachdem Sie ein paar Mal vondiesem Fehler gebissen wurden. So Sie das Maven Release Plugin benutzen, ist dieVerwaltung der Geschwister Versionen und deren Abhängigkeiten kein Thema,Versionen werden jeweils automatisch aktualisiert. Wenn Simple Web Version

1.3-Snapshot von Simple Persist Version 1.3-Snapshot abhängig ist, undSie geben beide Module in der Version 1.3 frei, so ist das Maven Release-Pluginschlau genug, um die Versionen innerhalb Ihrer gesamten multi-modularen ProjektPOMs automatisch anzupassen. Wenn Sie die Freigabe mittels dem Release-Pluginvornehmen werden automatisch alle Versionen in Ihrem Build auf 1.4-Snapshothochgezählt und das Release-Plugin wird darüber hinaus allen Code in dasRepository einchecken. Ein großes multi-modulares Projekt freizugeben könntealso nicht einfacher sein, bis ...

Probleme treten auf, wenn Entwickler Änderungen am POM einfliessen lassen,und sich in eine laufende Freigabe einmischen. Oftmals fügt ein Entwickler imKonfliktfall zwei Abhängigkeiten zusammen, oder agiert falsch im Fall eines

Optimirung und Überarbeitung der POMs

171

Page 191: Maven Definitive Guide De

Modul Konflikts, so dass er unwissentlich auf eine frühere Version zurückrollt. Dadie aufeinander folgenden Versionen der Abhängigkeit gewöhnlich kompatibelsind, zeigt sich dies nicht, wenn der Entwickler seinen Build erstellt, und es zeigtsich ebenso wenig in einem Continuous Integration Build-System. Stellen Sie sicheinen sehr komplexen Build vor, der Trunk besteht aus Komponenten des1.4-Snapshot, und jetzt müssen Sie sich vorstellen, dass Entwickler A eineKomponente tief in der Hierarchie des Projekts aktualisiert, welche von derVersion 1.3-Snapshot der Komponente B abhängt. Obwohl die meistenEntwickler bereits auf Version 1.4-Snapshot bauen, wird es beim Build keinenFehler geben, solange wie 1.3-SNAPSHOT und 1.4-SNAPSHOT der Komponente Bkompatibel sind. Maven wird auch weiterhin den Build auf der Basis der1.3-SNAPSHOT-Version von Komponente B ab dem lokalen Repository desEntwicklers durchführen. Alles scheint ganz reibungslos zu funktionieren, derBuild des Projektes läuft durch, der Continuous Integration Build funktioniertebenfalls, ein paar esoterische Fehler im Zusammenhang mit der Komponente B,aber diese werden auf das Werk hinterlistiger Kobolde abgetan. Mittlerweilepumpt eine Pumpe im Reaktor Raum und baut stetig Druck auf, bis schliesslichetwas bricht ...

Jemand, nennen wir ihn Herr Unachtsam, hatte beim Zusammenführen einenKonflikt in Komponente A, um diesen zu lösen, wurde irrtümlicherweise dieAbhängigkeit von Komponente A auf Komponente B Version 1.3-SNAPSHOT

gekoppelt, während der Rest des Projekts stets weiter marschiert. Ein GruppeEntwickler haben die ganze Zeit versucht, den Fehler in Komponente B zubeheben, und diese stehen vor einem Rätsel, warum Ihnen dies offenbar in derProduktion nicht gelingt. Schließlich befasst sich jemand mit Komponente A undrealisiert, dass die Abhängigkeit auf die falsche Version zeigt. Es bleibt zu hoffen,dass der Fehler nicht groß genug war, um Geld oder Leben zu kosten, aber HerrUnachtsam ist beschämt, und seine Kollegen vertrauen ihm ein bisschen wenigerals vor der ganzen Abhängigkeits-Sache. (Ich hoffe zwar, dass Herr Unachtsamrealisiert, dass es sich hier um einen Benutzerfehler handelt, und das Maven nichtSchuld war. Leider ist es mehr als wahrscheinlich Herr Unachtsam startet einefurchtbare Blog-Tirade und beschwert sich endlos über Maven, allein schon umsich besser zu fühlen.)

Optimirung und Überarbeitung der POMs

172

Page 192: Maven Definitive Guide De

Glücklicherweise ist die Weiderholung von Abhängigkeiten und dieUnstimmigkeit von Geschwisterversionen einfach vermeidbar. Es braucht nureinige kleine Veränderungen. Der erste Schritt ist, alle Abhängigkeiten welche inmehr als einem Projekt vorkommen zu finden und diese in das darüber stehendePOM im Abschnitt dependencyManagement aufzunehmen. Die GeschwisterAbhängigkeiten blenden wir für den Moment aus. Das Simple Parent POM

beinhaltet nun die folgenden Einträge:

<project>...<dependencyManagement>

<dependencies><dependency>

<groupId>org.springframework</groupId><artifactId>spring</artifactId><version>2.0.7</version>

</dependency><dependency>

<groupId>org.apache.velocity</groupId><artifactId>velocity</artifactId><version>1.5</version>

</dependency><dependency>

<groupId>org.hibernate</groupId><artifactId>hibernate-annotations</artifactId><version>3.3.0.ga</version>

</dependency><dependency>

<groupId>org.hibernate</groupId><artifactId>hibernate-commons-annotations</artifactId><version>3.3.0.ga</version>

</dependency><dependency>

<groupId>org.hibernate</groupId><artifactId>hibernate</artifactId><version>3.2.5.ga</version><exclusions>

<exclusion><groupId>javax.transaction</groupId><artifactId>jta</artifactId>

</exclusion></exclusions>

</dependency></dependencies>

</dependencyManagement>...

</project>

Sobald die Abhängigkeiten im übergeordneten POM eingetragen sind, müssen wir

Optimirung und Überarbeitung der POMs

173

Page 193: Maven Definitive Guide De

diese Einträge aus den 'Kind'-POM entfernen. Andernfalls werden die bestehendenEinträge die des übergeordneten Abschnitts dependencyManagement übersteuern.Der Einfachheit halber werden wir hier nur das Simple Model Modul POM zeigen:

<project>...<dependencies>

<dependency><groupId>org.hibernate</groupId><artifactId>hibernate-annotations</artifactId>

</dependency><dependency><groupId>org.hibernate</groupId><artifactId>hibernate</artifactId>

</dependency></dependencies>...

</project>

Das nächste, das wir angehen sollten, ist die Replikation derHibernate-Annotations sowie der Hibernate-Commons-Annotations, da dieseVersionen immer übereinstimmen sollten. Wir tun dies, indem wir ein Property mitdem Namen hibernate-annotations-version erstellen. Der daraus resultierendeAbschnitt des übergeordneten POM sieht wie folgt aus:

<project>...<properties>

<hibernate.annotations.version>3.3.0.ga</hibernate.annotations.version></properties>

<dependencyManagement>...<dependency><groupId>org.hibernate</groupId><artifactId>hibernate-annotations</artifactId><version>${hibernate.annotations.version}</version>

</dependency><dependency><groupId>org.hibernate</groupId><artifactId>hibernate-commons-annotations</artifactId><version>${hibernate.annotations.version}</version>

</dependency>...

</dependencyManagement>...

</project

Optimirung und Überarbeitung der POMs

174

Page 194: Maven Definitive Guide De

Zum Schluss nehmen wir uns noch der Geschwister-Abhängigkeit an. Einen Wegwelchen wir beschreiten könnten wäre, wie zuvor auch, diese Abhängigkeiten nachoben in das übergeordnete POM in den Abschnitt dependencyManagement zuverschieben, genau wie die Abhängigkeiten zuvor, mit einer definierten Versionender Geschwister-Projekte im Top-Level Projekt POM. Dies ist sicherlich eingültiges Konzept.

Allerdings können wir dieses Problem mittles dem Einsatz zweier eingebauterVariablen lösen: ${project.groupId} und ${project.version}. Da es sich umGeschwister Abhängigkeiten handelt, macht es nicht viel Sinn, einen numerischenWert im übergeordneten POM zu definieren, daher stützen wir uns auf dieeingebaute Eigenschaft ${project.version}. Da alle Geschwister derselbenGruppe angehören, können wir weitere Zukunftssicherheit einbauen, indem wir aufdas bestehende POM mittels der Eigenschaft ${project.groupId} verweisen. DerAbschnitt der Abhängigkeiten des Simple Command Moduls sieht nun wie folgtaus:

<project>...<dependencies>

...<dependency><groupId>${project.groupId}</groupId><artifactId>simple-weather</artifactId><version>${project.version}</version>

</dependency><dependency><groupId>${project.groupId}</groupId><artifactId>simple-persist</artifactId><version>${project.version}</version>

</dependency>...

</dependencies>...

</project>

Zusammenfassend hier die beiden Optimierungen welche wir angewandt haben,um Wiederholungen in der Deklaration der Abhängigkeiten zu reduzieren:

Ziehen Sie gemeinsame Abhängigkeiten in das übergeordnete POM in

den Abschnitt dependencyManagement

Optimirung und Überarbeitung der POMs

175

Page 195: Maven Definitive Guide De

Wenn mehr als ein Projekt von einer Abhängigkeit Gebrauch macht, könnenSie die Abhängigkeit im Element dependencyManagement des übergeordnetenPOM erfassen. Im übergeordneten POM werden Version und Ausschlüssegesetzt, in den untergeordneten POM müssen sie lediglich die KoordinatengroupId und artifactId angeben. Untergeordnete Projekte können die Versionsowie die Ausschlüsse weglassen, sollte die Abhängigkeit im ElementdependencyManagement deklariert sein.

Benutzen Sie auf die eingebauten Variablen 'version' und 'groupId'Um sich auf ein Geschwisterprojekt zu beziehen, verwenden Sie${project.version} und ${project.groupId}. Geschwister-Projekteumfassen fast immer die gleiche groupId und Release-Version. Mittels${project.version} entschärfen Sie das oben aufgeführte Problem derGeschwister-Version Missverhältnisse.

8.4. Optimirung der PluginsBetrachten wir die verschiedenen Plugin-Konfigurationen, können wir sehen, dassdie Abhängigkeiten zu HSQL DB an mehreren Orten wiederholt wird. Leider istdas Element dependencyManagement nicht auf Plugin-Abhängigkeiten anwendbar,dennoch können wir ein Property benutzen, um die verschiedenen Versionen zukonsolidieren. Tendeziell definieren komplexe multi-modulare Maven-Projektealle Versionen im Top-Level-POM. Dieses Top-Level-POM wird dann zu einemDreh-und Angelpunkt bezüglich allen Veränderungen, welche Auswirkungen aufdas gesamte Projekt haben. Stellen Sie sich die Version als Java String in einerJava Klasse vor; bei einer ständigen Wiederholung werden Sie wahrscheinlich eineVariable einsetzen wollen, damit, wenn es den String zu ändern gilt, Sie dies nuran einem Ort durchführen müssen. Das 'Hochziehen' der Version von HSQLDB inein Property des Top-Level-POM liefert uns fogenden Eintrag:

<project>...<properties>

<hibernate.annotations.version>3.3.0.ga</hibernate.annotations.version><hsqldb.version>1.8.0.7</hsqldb.version>

</properties>...

Optimirung und Überarbeitung der POMs

176

Page 196: Maven Definitive Guide De

</project>

Das nächste, was wir bemerken ist, dass dieHibernate3-Maven-Plugin-Konfiguration doppelt, sowohl im Simple Web-

sowie im Simple Command Modul vorkommt. Entsprechend der Abhängigkeitenkönnen wir die Plugin-Konfiguration in der Top-Level-POM Datei genau wieAbhängigkeiten im Element dependencyManagement definieren. Hierfür benutzenwir das Element pluginManagement in der Top Level POM Datei.

<project>...<build>

<pluginManagement><plugins>

<plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><configuration><source>1.5</source><target>1.5</target>

</configuration></plugin><plugin>

<groupId>org.codehaus.mojo</groupId><artifactId>hibernate3-maven-plugin</artifactId><version>2.1</version><configuration><components>

<component><name>hbm2ddl</name><implementation>annotationconfiguration</implementation>

</component></components>

</configuration><dependencies><dependency>

<groupId>hsqldb</groupId><artifactId>hsqldb</artifactId><version>${hsqldb.version}</version>

</dependency></dependencies>

</plugin></plugins>

</pluginManagement></build>...

</project>

Optimirung und Überarbeitung der POMs

177

Page 197: Maven Definitive Guide De

8.5. Optimierung unter Zuhilfenahmen desMaven Dependency PluginGrössere Projekte, haben eine Tendenz, dass mit wachsender AnzahlAbhängigkeiten auch zusätzliche Abhängigkeiten in ein POM einfliessen. Über dieZeit hinweg werden sich die Abhängigkeiten verändern, und so werden Sie überkurz oder lang auch verwaiste Abhängigkeiten beherbergen, genauso wie SieBibliotheken anziehen werden, welche nicht deklariert sind. Da Maven 2.xtransitive Abhängigkeiten im Geltungsbereich des Compilers einschliesst, kann espassieren, dass Ihr Projekt zwar fehlerfrei kompiliert, jedoch in der Produktionnicht lauffähig ist. Nehmen Sie zum Beispiel ein Projekt welches auf eine weitverbreitete Bibliothek wie Jakarta Commons BeanUtils aufsetzt. Statt dieseexplizit zu referenzieren, verlassen Sie sich auf die transitive Abhängigkeit, da Sieebenfalls Hibernate einsetzen, welches diese Library transitiv deklariert. IhrProjekt kompiliert und läuft wie erwartet, bis Sie eines Tages Hibernate updatenund hierbei auf eine Version wechseln, welche nicht mehr auf BeanUtils aufbaut.Plötzlich bekommen Sie viele Fehler, deren Grund nicht gerade offensichtlich ist.Darüber hinaus kann Maven auftretende Konflikte nicht lösen, da die Abhängigkeitnicht explizit deklariert wurde.

Eine bewährte Faustregel ist es, alle ausdrücklich im Code deklariertenAbhängigkeiten explizit zu deklarieren. Wenn Sie Jakarta Commons Beanutils

importieren, so sollten Sie diese auch explizit in der pom.xml-Datei festhalten.Glücklicherweise kann Ihnen hier Maven helfen, denn durch Bytecode Analyse istMaven in der Lage, solche direkten Abhängigkeiten aufzudecken. Lassen Sie unsdas zuvor optimierte POM auf Fehler durchleuchten:

$ mvn dependency:analyze[INFO] Scanning for projects...[INFO] Reactor build order:[INFO] Chapter 8 Simple Parent Project[INFO] Chapter 8 Simple Object Model[INFO] Chapter 8 Simple Weather API[INFO] Chapter 8 Simple Persistence API[INFO] Chapter 8 Simple Command Line Tool[INFO] Chapter 8 Simple Web Application[INFO] Chapter 8 Parent Project[INFO] Searching repository for plugin with prefix: 'dependency'.

Optimirung und Überarbeitung der POMs

178

Page 198: Maven Definitive Guide De

...

[INFO] ------------------------------------------------------------------------[INFO] Building Chapter 8 Simple Object Model[INFO] task-segment: [dependency:analyze][INFO] ------------------------------------------------------------------------[INFO] Preparing dependency:analyze[INFO] [resources:resources][INFO] Using default encoding to copy filtered resources.[INFO] [compiler:compile][INFO] Nothing to compile - all classes are up to date[INFO] [resources:testResources][INFO] Using default encoding to copy filtered resources.[INFO] [compiler:testCompile][INFO] Nothing to compile - all classes are up to date[INFO] [dependency:analyze][WARNING] Used undeclared dependencies found:[WARNING] javax.persistence:persistence-api:jar:1.0:compile[WARNING] Unused declared dependencies found:[WARNING] org.hibernate:hibernate-annotations:jar:3.3.0.ga:compile[WARNING] org.hibernate:hibernate:jar:3.2.5.ga:compile[WARNING] junit:junit:jar:3.8.1:test

...

[INFO] ------------------------------------------------------------------------[INFO] Building Chapter 8 Simple Web Application[INFO] task-segment: [dependency:analyze][INFO] ------------------------------------------------------------------------[INFO] Preparing dependency:analyze[INFO] [resources:resources][INFO] Using default encoding to copy filtered resources.[INFO] [compiler:compile][INFO] Nothing to compile - all classes are up to date[INFO] [resources:testResources][INFO] Using default encoding to copy filtered resources.[INFO] [compiler:testCompile][INFO] No sources to compile[INFO] [dependency:analyze][WARNING] Used undeclared dependencies found:[WARNING] org.sonatype.mavenbook.ch08:simple-model:jar:1.0:compile[WARNING] Unused declared dependencies found:[WARNING] org.apache.velocity:velocity:jar:1.5:compile[WARNING] javax.servlet:jstl:jar:1.1.2:compile[WARNING] taglibs:standard:jar:1.1.2:compile[WARNING] junit:junit:jar:3.8.1:test

Die vorangegangene, gekürzte Darstellung zeigt die Ausgabe des Goalsdependency:analyse. Dieses Goal analysisert, ob es irgendwelche indirektenAbhängigkeiten gibt, oder Abhängigkeiten, welche zwar referenziert, aber nicht

Optimirung und Überarbeitung der POMs

179

Page 199: Maven Definitive Guide De

deklariert werden. Im Projekt Simple Model zeigt das Plugin beispielhaft auf, wiedie Abhängigkeit javax.persistence:persistence-api eine "nicht deklarierte"Abhängigkeit darstellt. Um dies genauer zu untersuchen, welchseln Sie in dasVerzeichnis des Simple Model Projekt und rufen dort das Goal dependency:treeauf. Dies wird Ihnen eine Auflistung aller direkten und transitiven Abhängigkeitenausgeben.

$ mvn dependency:tree[INFO] Scanning for projects...[INFO] Searching repository for plugin with prefix: 'dependency'.[INFO] ------------------------------------------------------------------------[INFO] Building Chapter 8 Simple Object Model[INFO] task-segment: [dependency:tree][INFO] ------------------------------------------------------------------------[INFO] [dependency:tree][INFO] org.sonatype.mavenbook.ch08:simple-model:jar:1.0[INFO] +- org.hibernate:hibernate-annotations:jar:3.3.0.ga:compile[INFO] | \- javax.persistence:persistence-api:jar:1.0:compile[INFO] +- org.hibernate:hibernate:jar:3.2.5.ga:compile[INFO] | +- net.sf.ehcache:ehcache:jar:1.2.3:compile[INFO] | +- commons-logging:commons-logging:jar:1.0.4:compile[INFO] | +- asm:asm-attrs:jar:1.5.3:compile[INFO] | +- dom4j:dom4j:jar:1.6.1:compile[INFO] | +- antlr:antlr:jar:2.7.6:compile[INFO] | +- cglib:cglib:jar:2.1_3:compile[INFO] | +- asm:asm:jar:1.5.3:compile[INFO] | \- commons-collections:commons-collections:jar:2.1.1:compile[INFO] \- junit:junit:jar:3.8.1:test[INFO] ------------------------------------------------------------------------[INFO] BUILD SUCCESSFUL[INFO] ------------------------------------------------------------------------

Aus dieser Ausgabe, können wir ablesen, dass das bemängelte persistence-api

durch Hibernate eingeflossen ist. Ein flüchtiger Scan des Quellcodes aus diesemModul wird uns zeigen dass hier tatsächlich viele direkte Referenzen auf dieAbhängigkleit javax.persistence vorliegen. Die einfache Lösung besteht darineinen Verweis auf die Abhängigkeit in das POM aufzunehmen. In diesem Beispielsetzen wir die Abhängigkeit zur Version in den Abschnitt dependencyManagementdes POM von Simple Parent, da die Abhängikeit im Zusammenhang mitHibernate steht, und Hibernate dort deklariert wird. Irgendwann wird derZeipunkt gekommen sein, und Sie werden die Version von Hibernate upgraden.Die Nähe der Auflistung des persistence-api wird es Ihnen dann klareraufzeigen, dass hier eine Abhängigkeit besteht.

Optimirung und Überarbeitung der POMs

180

Page 200: Maven Definitive Guide De

Beim betrachten der Ausgabe von dependency:analyse des Simple Web Moduls

werden Sie feststellen, dass Sie eine Referenz zum Simple Model erstellen sollten.Ihr Quellcode in Simple Web referenziert direkt Objekte aus Simple Model

welches jedoch nur transitiv über Simple Persist eingebunden ist. Da es sich hierum eine Abhängigkeit unter Geschwistern handelt welche beide die gleichegroupId sowie version haben, können Sie die Abhängigkeit im POM von Simple

Web in der Form ${project.goupId} und ${project.version} vornehmen.

Wie konnte Maven diese Abhängigkeiten überhaupt aufdecken? Woher weissdependency:analyse überhaupt welche Klassen und andere Abhängigkeiten vonIhrem Bytecode referenziert werden? Das Dependency Plugin benutzt denObjectWeb ASM Toolkit um den Bytecode zu analysieren. Mittels ASMdurchläuft das Dependency Plugin den gesamten Code und erstellt einen Baumaller Klassen welche im aktuellen Projekt referenziert werden. Anschliessenddurchläuft das Werkzeug alle deklarierten und transitiven Abhängigkeiten undsteicht alle Klassen welche den direkten Abhängigkeiten zugeordnet werdenkönnen. Klassen welche nicht Teil der direkten Abhängigkeitsliste sind, müssensomit der transitiven Abhängigkeitsliste entspringen. Diese können alseingebundene, jedoch nicht deklarierte Abhängigkeit (used, undeclareddependencies) ausgegeben werden.

Im Gegensatz dazu ist die Liste der deklarierten, aber nicht eingesetztenAbhängigkeiten ungleich schwieriger zu validieren, und weit weniger nützlich alsdie der gebrauchten, nicht deklarierternAbhängigkeiten. Zum einen werdenmanche der Abhängigkeiten nur während der Laufzeit oder in Tests angezogen,diese sind im Bytecode nicht ersichtlich. Beim Betrachten der Liste wird diesoffensichtlich: so ist zum Beispiel JUnit Teil der Liste, was zu erwarten ist, da esnur zum Test eingesetzt wird. Sie werden auch feststellen, dass Velocity sowiedas Servlet API auf der Liste der Abhängigkeiten des Simpel Web Moduls

erscheint. Das ist nicht weiter verwunderlich, da während das Projekt keinedirekten Verweise auf die Klassen dieser Artefakte hat, diese nach wie vor imlaufenden Betrieb unverzichtbar sind.

Seien Sie vorsichtig mit dem Entfernen unbenutzer, deklarierter Abhängigkeiten(unused, declared dependencies), denn ausser Sie haben eine sehr guteTestabdeckung fügen Sie hierbei leicht Laufzeitfehler ein. Eine noch bösere

Optimirung und Überarbeitung der POMs

181

Page 201: Maven Definitive Guide De

Überraschung rührt von Bytecode Optimierern her: So ist es zum Beispiel legalden Wert einer Konstanten zu ersetzen um damit die bestehende Referenzwegzuoptimieren. Die Entfernung dieser Abhängigkeit führt jedoch dazu, dass dieKompilierung scheitert, obschon das Tool zeigt, dass diese ungenutzt ist. Es istabzusehen, dass zukünftige Versionen des Dependency Maven-Plugin wirdverbesserte Techniken zur Erkennung solcherlei Problemstellungen bereitstellenwerden.

Es ist zu empfehlen, dass Sie ab und zu dependency:analyse ausführen, umsolcherlei Fehler des POM aufzudecken. Das Goal kann derart konfigueriertwerden, dass beim Auftreten bestimmter Konditionen der Build abbricht, alternativwird ein Report bereitgestellt.

8.6. Abschliessende POMsAbschliessend sollen hier nun die endgültigen, optimierten pom.xml-Dateien alsReferenz für dieses Kapitel widergegeben werden. Hier ist das Top-Level POM fürSimple Parent:

Example 8.1. Abschliessendes POM des Moduls Simple-Parent

<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/maven-v4_0_0.xsd"><modelVersion>4.0.0</modelVersion>

<groupId>org.sonatype.mavenbook.ch08</groupId><artifactId>simple-parent</artifactId><packaging>pom</packaging><version>1.0</version><name>Chapter 8 Simple Parent Project</name>

<modules><module>simple-command</module><module>simple-model</module><module>simple-weather</module><module>simple-persist</module><module>simple-webapp</module>

</modules>

<build>

Optimirung und Überarbeitung der POMs

182

Page 202: Maven Definitive Guide De

<pluginManagement><plugins>

<plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><configuration><source>1.5</source><target>1.5</target>

</configuration></plugin><plugin>

<groupId>org.codehaus.mojo</groupId><artifactId>hibernate3-maven-plugin</artifactId><version>2.1</version><configuration><components>

<component><name>hbm2ddl</name><implementation>annotationconfiguration</implementation>

</component></components>

</configuration><dependencies><dependency>

<groupId>hsqldb</groupId><artifactId>hsqldb</artifactId><version>${hsqldb.version}</version>

</dependency></dependencies>

</plugin></plugins>

</pluginManagement></build>

<properties><hibernate.annotations.version>3.3.0.ga</hibernate.annotations.version><hsqldb.version>1.8.0.7</hsqldb.version>

</properties><dependencyManagement>

<dependencies><dependency>

<groupId>org.springframework</groupId><artifactId>spring</artifactId><version>2.0.7</version>

</dependency><dependency>

<groupId>org.apache.velocity</groupId><artifactId>velocity</artifactId><version>1.5</version>

</dependency><dependency>

<groupId>javax.persistence</groupId><artifactId>persistence-api</artifactId>

Optimirung und Überarbeitung der POMs

183

Page 203: Maven Definitive Guide De

<version>1.0</version></dependency><dependency>

<groupId>org.hibernate</groupId><artifactId>hibernate-annotations</artifactId><version>${hibernate.annotations.version}</version>

</dependency><dependency>

<groupId>org.hibernate</groupId><artifactId>hibernate-commons-annotations</artifactId><version>${hibernate.annotations.version}</version>

</dependency><dependency>

<groupId>org.hibernate</groupId><artifactId>hibernate</artifactId><version>3.2.5.ga</version><exclusions>

<exclusion><groupId>javax.transaction</groupId><artifactId>jta</artifactId>

</exclusion></exclusions>

</dependency></dependencies>

</dependencyManagement>

<dependencies><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>3.8.1</version><scope>test</scope>

</dependency></dependencies>

</project>

The POM shown in Example 8.2, “Abschliessendes POM des MoulsSimple-Command” captures the POM for simple-command, the command-lineversion of the tool.

Die folgenden POM erfasst das Simple Command Tool, die Kommando-ZeilenVersion des Tools (Dienstprogramm).

Example 8.2. Abschliessendes POM des Mouls Simple-Command

<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

Optimirung und Überarbeitung der POMs

184

Page 204: Maven Definitive Guide De

http://maven.apache.org/maven-v4_0_0.xsd"><modelVersion>4.0.0</modelVersion><parent>

<groupId>org.sonatype.mavenbook.ch08</groupId><artifactId>simple-parent</artifactId><version>1.0</version>

</parent>

<artifactId>simple-command</artifactId><packaging>jar</packaging><name>Chapter 8 Simple Command Line Tool</name>

<build><pluginManagement><plugins>

<plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-jar-plugin</artifactId><configuration><archive>

<manifest><mainClass>org.sonatype.mavenbook.weather.Main</mainClass><addClasspath>true</addClasspath>

</manifest></archive>

</configuration></plugin><plugin>

<groupId>org.apache.maven.plugins</groupId><artifactId>maven-surefire-plugin</artifactId><configuration><testFailureIgnore>true</testFailureIgnore>

</configuration></plugin><plugin><artifactId>maven-assembly-plugin</artifactId><configuration><descriptorRefs>

<descriptorRef>jar-with-dependencies</descriptorRef></descriptorRefs>

</configuration></plugin>

</plugins></pluginManagement>

</build>

<dependencies><dependency><groupId>${project.groupId}</groupId><artifactId>simple-weather</artifactId><version>${project.version}</version>

</dependency><dependency>

Optimirung und Überarbeitung der POMs

185

Page 205: Maven Definitive Guide De

<groupId>${project.groupId}</groupId><artifactId>simple-persist</artifactId><version>${project.version}</version>

</dependency><dependency><groupId>org.springframework</groupId><artifactId>spring</artifactId>

</dependency><dependency><groupId>org.apache.velocity</groupId><artifactId>velocity</artifactId>

</dependency></dependencies>

</project>

Darauf folgend das POM des Simple Model Projekt. Simple Model beinhaltetalle Objekt Modelle der gesamten Anwendung.

Example 8.3. Abschlissendes POM des Moduls Simple-Model

<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/maven-v4_0_0.xsd"><modelVersion>4.0.0</modelVersion><parent>

<groupId>org.sonatype.mavenbook.ch08</groupId><artifactId>simple-parent</artifactId><version>1.0</version>

</parent><artifactId>simple-model</artifactId><packaging>jar</packaging>

<name>Chapter 8 Simple Object Model</name>

<dependencies><dependency><groupId>org.hibernate</groupId><artifactId>hibernate-annotations</artifactId>

</dependency><dependency><groupId>org.hibernate</groupId><artifactId>hibernate</artifactId>

</dependency><dependency><groupId>javax.persistence</groupId><artifactId>persistence-api</artifactId>

</dependency></dependencies>

Optimirung und Überarbeitung der POMs

186

Page 206: Maven Definitive Guide De

</project>

Im folgenden das POM des Simple Persist Moduls. Hierin wird die gesamtePersistenzlogik durch Hibernate gekapselt.

Example 8.4. Abschliessendes POM fdes Moduls Simple-Persist

<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/maven-v4_0_0.xsd"><modelVersion>4.0.0</modelVersion><parent>

<groupId>org.sonatype.mavenbook.ch08</groupId><artifactId>simple-parent</artifactId><version>1.0</version>

</parent><artifactId>simple-persist</artifactId><packaging>jar</packaging>

<name>Chapter 8 Simple Persistence API</name>

<dependencies><dependency><groupId>${project.groupId}</groupId><artifactId>simple-model</artifactId><version>${project.version}</version>

</dependency><dependency><groupId>org.hibernate</groupId><artifactId>hibernate</artifactId>

</dependency><dependency><groupId>org.hibernate</groupId><artifactId>hibernate-annotations</artifactId>

</dependency><dependency><groupId>org.hibernate</groupId><artifactId>hibernate-commons-annotations</artifactId>

</dependency><dependency><groupId>javax.servlet</groupId><artifactId>servlet-api</artifactId><version>2.4</version><scope>provided</scope>

</dependency><dependency><groupId>org.springframework</groupId><artifactId>spring</artifactId>

Optimirung und Überarbeitung der POMs

187

Page 207: Maven Definitive Guide De

</dependency></dependencies>

</project>

Dann das Simple Weather Projekt, das Projekt, welches die gesamte Arbeit mitdem Yahoo! Wetter RSS-Feed und dessen Analyse zu tun hat. Dieses Projekt istvon Simple Model abhängig.

Example 8.5. Abschlissendes POM des Moduls Simple-Weather

<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/maven-v4_0_0.xsd"><modelVersion>4.0.0</modelVersion><parent>

<groupId>org.sonatype.mavenbook.ch08</groupId><artifactId>simple-parent</artifactId><version>1.0</version>

</parent><artifactId>simple-weather</artifactId><packaging>jar</packaging>

<name>Chapter 8 Simple Weather API</name>

<dependencies><dependency><groupId>${project.groupId}</groupId><artifactId>simple-model</artifactId><version>${project.version}</version>

</dependency><dependency><groupId>log4j</groupId><artifactId>log4j</artifactId><version>1.2.14</version>

</dependency><dependency><groupId>dom4j</groupId><artifactId>dom4j</artifactId><version>1.6.1</version>

</dependency><dependency><groupId>jaxen</groupId><artifactId>jaxen</artifactId><version>1.1.1</version>

</dependency><dependency><groupId>org.apache.commons</groupId><artifactId>commons-io</artifactId>

Optimirung und Überarbeitung der POMs

188

Page 208: Maven Definitive Guide De

<version>1.3.2</version><scope>test</scope>

</dependency></dependencies>

</project>

Schließlich ist das Simple Web Projekt, eine Web-Applikation welche dieabgerufenen Wettervorhersagen in einer HSQLDB Datenbank speichert, sowie mitSimple Weather und den daraus bereitgestellten Bibliotheken kommuniziert.

Example 8.6. Abschliessendes POM des Moduls Simple-Webapp

<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/maven-v4_0_0.xsd"><modelVersion>4.0.0</modelVersion><parent>

<groupId>org.sonatype.mavenbook.ch08</groupId><artifactId>simple-parent</artifactId><version>1.0</version>

</parent>

<artifactId>simple-webapp</artifactId><packaging>war</packaging><name>Chapter 8 Simple Web Application</name><dependencies>

<dependency><groupId>javax.servlet</groupId><artifactId>servlet-api</artifactId><version>2.4</version><scope>provided</scope>

</dependency><dependency><groupId>${project.groupId}</groupId><artifactId>simple-model</artifactId><version>${project.version}</version>

</dependency><dependency><groupId>${project.groupId}</groupId><artifactId>simple-weather</artifactId><version>${project.version}</version>

</dependency><dependency><groupId>${project.groupId}</groupId><artifactId>simple-persist</artifactId><version>${project.version}</version>

</dependency><dependency>

Optimirung und Überarbeitung der POMs

189

Page 209: Maven Definitive Guide De

<groupId>org.springframework</groupId><artifactId>spring</artifactId>

</dependency><dependency><groupId>javax.servlet</groupId><artifactId>jstl</artifactId><version>1.1.2</version>

</dependency><dependency><groupId>taglibs</groupId><artifactId>standard</artifactId><version>1.1.2</version>

</dependency><dependency><groupId>org.apache.velocity</groupId><artifactId>velocity</artifactId>

</dependency></dependencies><build>

<finalName>simple-webapp</finalName><plugins><plugin>

<groupId>org.mortbay.jetty</groupId><artifactId>maven-jetty-plugin</artifactId><version>6.1.9</version><dependencies>

<dependency><groupId>hsqldb</groupId><artifactId>hsqldb</artifactId><version>${hsqldb.version}</version>

</dependency></dependencies>

</plugin></plugins>

</build></project>

8.7. FazitDieses Kapitel hat aufgezeigt, welche Techniken Ihnen zur Verbesserung derKontrolle Ihrer Abhängigkeiten und Plugins zur Verfügung stehen, um Ihrezukünftigen Builds wartungsfreundlich zu gestalten. Wir empfehlen inregelmäßigen Abständen der Überprüfung Ihres Builds wie aufgezeigt, umsicherzustellen, dass Wiederholungen und damit verbundene potenzielleKrisenherde minimiert werden. Wie Ihr Projekt reift, werden Sie zwangsläufig

Optimirung und Überarbeitung der POMs

190

Page 210: Maven Definitive Guide De

neue Abhängigkeiten einführen, oder Sie werden feststellen, dass eineEinzelabhängigkeit nun an 10 oder mehr Orten zum Einsatz kommt und somitbesser höher aufgehängt wird. Die Liste der zum Einsatz kommenden bzw. nichtkommenden Abhängigkeiten verändert sich im Laufe der Zeit und lässt sich mitdem Dependency Maven Plugin leicht bearbeiten.

Optimirung und Überarbeitung der POMs

191

Page 211: Maven Definitive Guide De

Part II. Maven Reference

192

Page 212: Maven Definitive Guide De

Chapter 9. The Project Object Model

9.1. IntroductionThis chapter covers the central concept of Maven—the Project Object Model. ThePOM is where a project’s identity and structure are declared, builds are configured,and projects are related to one another. The presence of a pom.xml file defines aMaven project.

9.2. The POMMaven projects, dependencies, builds, artifacts: all of these are objects to bemodeled and described. These objects are described by an XML file called aProject Object Model. The POM tells Maven what sort of project it is dealing withand how to modify default behavior to generate output from source. In the sameway a Java web application has a web.xml that describes, configures, andcustomizes the application, a Maven project is defined by the presence of apom.xml. It is a descriptive declaration of a project for Maven; it is the figurative“map” that Maven needs to understand what it is looking at when it builds yourproject.

You could also think of the pom.xml as analogous to a Makefile or an Antbuild.xml. When you are using GNU make to build something like MySQL,you’ll usually have a file named Makefile that contains explicit instructions forbuilding a binary from source. When you are using Apache Ant, you likely have afile named build.xml that contains explicit instructions for cleaning, compiling,packaging, and deploying an application. make, Ant, and Maven are similar in thatthey rely on the presence of a commonly named file such as Makefile, build.xml,or pom.xml, but that is where the similarities end. If you look at a Maven pom.xml,the majority of the POM is going to deal with descriptions: Where is the sourcecode? Where are the resources? What is the packaging? If you look at an Antbuild.xml file, you’ll see something entirely different. You’ll see explicit

193

Page 213: Maven Definitive Guide De

instructions for tasks such as compiling a set of Java classes. The Maven POM isdeclarative, and although you can certainly choose to include some proceduralcustomizations via the Maven Ant plugin, for the most part you will not need to getinto the gritty procedural details of your project’s build.

The POM is also not specific to building Java projects. While most of the examplesin this book are geared towards Java applications, there is nothing Java-specific inthe definition of a Maven Project Object Model. While Maven's default plugins aretargeted at building JAR artifacts from a set of source, tests, and resources, there isnothing preventing you from defining a POM for a project that contains C# sourcesand produces some proprietary Microsoft binary using Microsoft tools. Similarly,there is nothing stopping you from defining a POM for a technical book. In fact,the source for this book and this book's examples is captured in a multi-moduleMaven project which uses one of the many Maven Docbook plugins to apply thestandard Docbook XSL to a series of chapter XML files. Others have createdMaven plugins to build Adobe Flex code into SWCs and SWFs, and yet othershave used Maven to build projects written in C.

We've established that the POM describes and declares, it is unlike Ant or Make inthat it doesn't provide explicit instructions, and we've noted that POM concepts arenot specific to Java. Diving into more specifics, take a look at Figure 9.1, “TheProject Object Model” for a survey of the contents of a POM.

The Project Object Model

194

Page 214: Maven Definitive Guide De

Figure 9.1. The Project Object Model

The POM contains four categories of description and configuration:

General project informationThis includes a project’s name, the URL for a project, the sponsoringorganization, and a list of developers and contributors along with the license fora project.

Build settingsIn this section, we customize the behavior of the default Maven build. We canchange the location of source and tests, we can add new plugins, we can attachplugin goals to the lifecycle, and we can customize the site generationparameters.

Build environmentThe build environment consists of profiles that can be activated for use indifferent environments. For example, during development you may want todeploy to a development server, whereas in production you want to deploy to a

The Project Object Model

195

Page 215: Maven Definitive Guide De

production server. The build environment customizes the build settings forspecific environments and is often supplemented by a custom settings.xml in~/.m2. This settings file is discussed in Chapter 11, Build Profiles and in thesection Section A.2, “Die Details der settings.xml Datei”.

POM relationshipsA project rarely stands alone; it depends on other projects, inherits POMsettings from parent projects, defines its own coordinates, and may includesubmodules.

9.2.1. The Super POMBefore we dive into some examples of POMs, let's take a quick look at the SuperPOM. All Maven project POMs extend the Super POM which defines a set ofdefaults shared by all projects. This Super POM is a part of the Maven installation,and can be found in the maven-2.0.9-uber.jar file in ${M2_HOME}/lib. If youlook in this JAR file, you will find a file named pom-4.0.0.xml under theorg.apache.maven.project package. The Super POM for Maven is shown inExample 9.1, “The Super POM”.

Example 9.1. The Super POM

<project><modelVersion>4.0.0</modelVersion><name>Maven Default Project</name>

<repositories><repository><id>central</id> #<name>Maven Repository Switchboard</name><layout>default</layout><url>http://repo1.maven.org/maven2</url><snapshots>

<enabled>false</enabled></snapshots>

</repository></repositories>

<pluginRepositories><pluginRepository><id>central</id> #<name>Maven Plugin Repository</name>

The Project Object Model

196

Page 216: Maven Definitive Guide De

<url>http://repo1.maven.org/maven2</url><layout>default</layout><snapshots>

<enabled>false</enabled></snapshots><releases>

<updatePolicy>never</updatePolicy></releases>

</pluginRepository></pluginRepositories>

<build> #<directory>target</directory><outputDirectory>target/classes</outputDirectory><finalName>${pom.artifactId}-${pom.version}</finalName><testOutputDirectory>target/test-classes</testOutputDirectory><sourceDirectory>src/main/java</sourceDirectory><scriptSourceDirectory>src/main/scripts</scriptSourceDirectory><testSourceDirectory>src/test/java</testSourceDirectory><resources><resource>

<directory>src/main/resources</directory></resource>

</resources><testResources><testResource>

<directory>src/test/resources</directory></testResource>

</testResources></build>

<pluginManagement>#<plugins><plugin>

<artifactId>maven-antrun-plugin</artifactId><version>1.1</version>

</plugin><plugin>

<artifactId>maven-assembly-plugin</artifactId><version>2.2-beta-1</version>

</plugin><plugin>

<artifactId>maven-clean-plugin</artifactId><version>2.2</version>

</plugin><plugin>

<artifactId>maven-compiler-plugin</artifactId><version>2.0.2</version>

</plugin><plugin>

<artifactId>maven-dependency-plugin</artifactId><version>2.0</version>

</plugin>

The Project Object Model

197

Page 217: Maven Definitive Guide De

<plugin><artifactId>maven-deploy-plugin</artifactId><version>2.3</version>

</plugin><plugin>

<artifactId>maven-ear-plugin</artifactId><version>2.3.1</version>

</plugin><plugin>

<artifactId>maven-ejb-plugin</artifactId><version>2.1</version>

</plugin><plugin>

<artifactId>maven-install-plugin</artifactId><version>2.2</version>

</plugin><plugin>

<artifactId>maven-jar-plugin</artifactId><version>2.2</version>

</plugin><plugin>

<artifactId>maven-javadoc-plugin</artifactId><version>2.4</version>

</plugin><plugin>

<artifactId>maven-plugin-plugin</artifactId><version>2.3</version>

</plugin><plugin>

<artifactId>maven-rar-plugin</artifactId><version>2.2</version>

</plugin><plugin>

<artifactId>maven-release-plugin</artifactId><version>2.0-beta-7</version>

</plugin><plugin>

<artifactId>maven-resources-plugin</artifactId><version>2.2</version>

</plugin><plugin>

<artifactId>maven-site-plugin</artifactId><version>2.0-beta-6</version>

</plugin><plugin>

<artifactId>maven-source-plugin</artifactId><version>2.0.4</version>

</plugin><plugin>

<artifactId>maven-surefire-plugin</artifactId><version>2.4.2</version>

</plugin><plugin>

The Project Object Model

198

Page 218: Maven Definitive Guide De

<artifactId>maven-war-plugin</artifactId><version>2.1-alpha-1</version>

</plugin></plugins>

</pluginManagement>

<reporting><outputDirectory>target/site</outputDirectory>

</reporting></project>

The Super POM defines some standard configuration variables that are inheritedby all projects. Those values are captured in the annotated sections:

❶ The default Super POM defines a single remote Maven repository with an IDof central. This is the central Maven repository that all Maven clients areconfigured to read from by default. This setting can be overridden by acustom settings.xml file. Note that the default Super POM has disabledsnapshot artifacts on the central Maven repository. If you need to use asnapshot repository, you will need to customize repository settings in yourpom.xml or in your settings.xml. Settings and profiles are covered inChapter 11, Build Profiles and in Section A.2, “Die Details der settings.xmlDatei”.

❷ The central Maven repository also contains Maven plugins. The defaultplugin repository is the central Maven repository. Snapshots are disabled, andthe update policy is set to “never,” which means that Maven will neverautomatically update a plugin if a new version is released.

❸ The build element sets the default values for directories in the MavenStandard Directory layout.

❹ Starting in Maven 2.0.9, default versions of core plugins have been providedin the Super POM. This was done to provide some stability for users that arenot specifying versions in their POMs.

The Project Object Model

199

Page 219: Maven Definitive Guide De

Figure 9.2. The Super POM is always the base Parent

9.2.2. The Simplest POMAll Maven POMs inherit defaults from the Super POM (introduced earlier in thesection Section 9.2.1, “The Super POM””). If you are just writing a simple projectthat produces a JAR from some source in src/main/java, want to run your JUnittests in src/test/java, and want to build a project site using mvn site, you don’thave to customize anything. All you would need, in this case, is the simplestpossible POM shown in Example 9.2, “The Simplest POM”. This POM defines agroupId, artifactId, and version: the three required coordinates for everyproject.

Example 9.2. The Simplest POM

<project><modelVersion>4.0.0</modelVersion><groupId>org.sonatype.mavenbook.ch08</groupId><artifactId>simplest-project</artifactId><version>1</version>

</project>

The Project Object Model

200

Page 220: Maven Definitive Guide De

Such a simple POM would be more than adequate for a simple project—e.g., aJava library that produces a JAR file. It isn’t related to any other projects, it has nodependencies, and it lacks basic information such as a name and a URL. If youwere to create this file and then create the subdirectory src/main/java with somesource code, running mvn package would produce a JAR intarget/simple-project-1.jar.

9.2.3. The Effective POMThis simplest POM brings us to the concept of the “effective POM.” Since POMscan inherit configuration from other POMs, you must always think of a MavenPOM in terms of the combination of the Super POM, plus any parent POMs, andfinally the current project’s POM. Maven starts with the Super POM and thenoverrides default configuration with one or more parent POMs. Then it overridesthe resulting configuration with the current project’s POM. You end up with aneffective POM that is a mixture of various POMs. If you want to see a project’seffective POM, you’ll need to run the effective-pom goal in the Maven Helpplugin, which was introduced earlier in the section Section 2.8, “Das Maven HilfePlugin”.” To run the effective-pom goal, execute the following in a directory witha pom.xml file:

$ mvn help:effective-pom

Executing the effective-pom goal should print out an XML document capturingthe merge between the Super POM and the POM from Example 9.2, “The SimplestPOM”.

9.2.4. Real POMsInstead of typing up a contrived set of POMs to walk you through step-by-step,you should take a look at the examples in Part I, “Maven by Example”. Maven issomething of a chameleon; you can pick and choose the features you want to takeadvantage of. Some open source projects may value the ability to list developersand contributors, generate clean project documentation, and manage releasesautomatically using the Maven Release plugin. On the other hand, someone

The Project Object Model

201

Page 221: Maven Definitive Guide De

working in a corporate environment on a small team might not be interested in thedistribution management capabilities of Maven nor the ability to list developers.The remainder of this chapter is going to discuss features of the POM in isolation.Instead of bombarding you with a 10-page listing of a set of related POMs, we’regoing to focus on creating a good reference for specific sections of the POM. Inthis chapter, we discuss relationships between POMs, but we don’t illustrate such aproject here. If you are looking for such an illustration, refer to Chapter 7,Multi-module Enterprise Project.

9.3. POM SyntaxThe POM is always in a file named pom.xml in the base directory of a Mavenproject. This XML document can start with the XML declaration, or you canchoose to omit it. All values in a POM are captured as XML elements.

9.3.1. Project VersionsA Maven project’s version encodes a release version number that is used to groupand order releases. Maven versions contain the following parts: major version,minor version, incremental version, and qualifier. In a version, these partscorrespond to the following format:

<major version>.<minor version>.<incremental version>-<qualifier>

For example, the version "1.3.5" has a major version of 1, a minor version of 3,and an incremental version of 5. The version "5" has a major version of 5 and nominor or incremental version. The qualifier exists to capture milestone builds:alpha and beta releases, and the qualifier is separated from the major, minor, andincremental versions by a hyphen. For example, the version "1.3-beta-01" has amajor version of 1, a minor version of 3, and a qualifier of "beta-01".

Keeping your version numbers aligned with this standard will become veryimportant when you want to start using version ranges in your POMs. Versionranges, introduced in Section 9.4.3, “Dependency Version Ranges”, allow you tospecify a dependency on a range of versions, and they are only supported because

The Project Object Model

202

Page 222: Maven Definitive Guide De

Maven has the ability to sort versions based on the version release number formatintroduced in this section.

If your version release number matches the format<major>.<minor>.<incremental>-<qualifier> then your versions will becompared properly; "1.2.3" will be evaluated as a more recent build than "1.0.2",and the comparison will be made using the numeric values of the major, minor,and incremental versions. If your version release number does not fit the standardintroduced in this section, then your versions will be compared as strings; "1.0.1b"will be compared to "1.2.0b" using a String comparison.

9.3.1.1. Version Build NumbersOne gotcha for release version numbers is the ordering of the qualifiers. Take theversion release numbers “1.2.3-alpha-2” and “1.2.3-alpha-10,” where the “alpha-2”build corresponds to the 2nd alpha build, and the “alpha-10” build corresponds tothe 10th alpha build. Even though “alpha-10” should be considered more recentthan “alpha-2,” Maven is going to sort “alpha-10” before “alpha-2” due to a knownissue in the way Maven handles version numbers.

Maven is supposed to treat the number after the qualifier as a build number. Inother words, the qualifier should be "alpha", and the build number should be 2.Even though Maven has been designed to separate the build number from thequalifier, this parsing is currently broken. As a result, "alpha-2" and "alpha-10" arecompared using a String comparison, and "alpha-10" comes before "alpha-2"alphabetically. To get around this limitation, you will need to left-pad yourqualified build numbers. If you use "alpha-02" and "alpha-10" this problem will goaway, and it will continue to work once Maven properly parses the version buildnumber.

9.3.1.2. SNAPSHOT VersionsMaven versions can contain a string literal to signify that a project is currentlyunder active development. If a version contains the string “SNAPSHOT,” thenMaven will expand this token to a date and time value converted to UTC(Coordinated Universal Time) when you install or release this component. For

The Project Object Model

203

Page 223: Maven Definitive Guide De

example, if your project has a version of “1.0-SNAPSHOT” and you deploy thisproject’s artifacts to a Maven repository, Maven would expand this version to“1.0-20080207-230803-1” if you were to deploy a release at 11:08 PM onFebruary 7th, 2008 UTC. In other words, when you deploy a snapshot, you are notmaking a release of a software component; you are releasing a snapshot of acomponent at a specific time.

Why would you use this? SNAPSHOT versions are used for projects under activedevelopment. If your project depends on a software component that is under activedevelopment, you can depend on a SNAPSHOT release, and Maven willperiodically attempt to download the latest snapshot from a repository when yourun a build. Similarly, if the next release of your system is going to have a version"1.4", your project would have a version "1.4-SNAPSHOT" version until it wasformally released.

As a default setting, Maven will not check for SNAPSHOT releases on remoterepositories, to depend on SNAPSHOT releases, users must explicitly enable theability to download snapshots using a repository or pluginRepository elementin the POM.

When releasing a project you should resolve all dependencies on SNAPSHOTversions to dependencies on released versions. If a project depends on aSNAPSHOT, it is not stable as the dependencies may change over time. Artifactspublished to non-snapshot Maven repositories such ashttp://repo1.maven.org/maven2 cannot depend on SNAPSHOT versions, asMaven's Super POM has snapshot's disabled from the Central repository.SNAPSHOT versions are for development only.

9.3.1.3. LATEST and RELEASE VersionsWhen you depend on a plugin or a dependency, you can use the a version value ofLATEST or RELEASE. LATEST refers to the latest released or snapshot versionof a particular artifact, the most recently deployed artifact in a particularrepository. RELEASE refers to the last non-snapshot release in the repository. Ingeneral, it is not a best practice to design software which depends on a non-specificversion of an artifact. If you are developing software, you might want to use

The Project Object Model

204

Page 224: Maven Definitive Guide De

RELEASE or LATEST as a convenience so that you don't have to update versionnumbers when a new release of a third-party library is released. When you releasesoftware, you should always make sure that your project depends on specificversions to reduce the chances of your build or your project being affected by asoftware release not under your control. Use LATEST and RELEASE withcaution, if at all.

Starting with Maven 2.0.9, Maven locks down the version numbers of commonand core Maven plugins in the super POM to standardize a core set of Mavenplugins for a particular version of Maven. This change was introduced to Maven2.0.9 to bring stability and reproducibility to Maven builds. Before Maven 2.0.9,Maven would automatically update core Maven plugins using the LATESTversion. This behavior led to a number of surprises when bugs was introduced intocore plugins or functionality changed in a core plugin which subsequently broke abuild. When Maven automatically updated core plugins, it was noted that there waslittle guarantee that a build would be reproducible as plugins could be updatedwhenever a new version was pushed to the central repository. Starting with Maven2.0.9, Maven, essentially, ships with a core set of locked down plugin versions.Non-core plugins, or plugins without versions assigned in the Super POM will stilluse the LATEST version to retrieve a plugin artifact from the repository. It is forthis reason that you should assign explicit version numbers to any custom ornon-core plugins used in your build.

9.3.2. Property ReferencesA POM can include references to properties preceded by a dollar sign andsurrounded by two curly braces. For example, consider the following POM:

<project><modelVersion>4.0.0</modelVersion><groupId>org.sonatype.mavenbook</groupId><artifactId>project-a</artifactId><version>1.0-SNAPSHOT</version><packaging>jar</packaging><build>

<finalName>${project.groupId}-${project.artifactId}</finalName></build>

</project>

The Project Object Model

205

Page 225: Maven Definitive Guide De

If you put this XML in a pom.xml and run mvn help:effective-pom, you will seethat the output contains the line:

...<finalName>org.sonatype.mavenbook-project-a</finalName>...

When Maven reads a POM, it replaces references to properties when it loads thePOM XML. Maven properties occur frequently in advanced Maven usage, and aresimilar to properties in other systems such as Ant or Velocity. They are simplyvariables delimited by ${...}. Maven provides three implicit variables which canbe used to access environment variables, POM information, and Maven Settings:

envThe env variable exposes environment variables exposed by your operatingsystem or shell. For example, a reference to ${env.PATH} in a Maven POMwould be replaced by the ${PATH} environment variable (or %PATH% inWindows).

projectThe project variable exposes the POM. You can use a dot-notated (.) path toreference the value of a POM element. For example, in this section we used thegroupId and artifactId to set the finalName element in the buildconfiguration. The syntax for this property reference was:${project.groupId}-${project.artifactId}.

settingsThe settings variable exposes Maven settings information. You can use adot-notated (.) path to reference the value of an element in a settings.xml file.For example, ${settings.offline} would reference the value of the offline

element in ~/.m2/settings.xml.

NoteYou may see older builds that use ${pom.xxx} or just ${xxx} to referencePOM properties. These methods have been deprecated and only${project.xxx} should be used.

The Project Object Model

206

Page 226: Maven Definitive Guide De

In addition to the three implicit variables, you can reference system properties andany custom properties set in the Maven POM or in a build profile:

Java System PropertiesAll properties accessible via getProperties() on java.lang.System areexposed as POM properties. Some examples of system properties are:${user.name}, ${user.home}, ${java.home}, and ${os.name}. A full list ofsystem properties can be found in the Javadoc for the java.lang.System class.

xArbitrary properties can be set with a properties element in a pom.xml orsettings.xml, or properties can be loaded from external files. If you set aproperty named fooBar in your pom.xml, that same property is referenced with${fooBar}. Custom properties come in handy when you are building a systemthat filters resources and targets different deployment platforms. Here is thesyntax for setting ${foo}=bar in a POM:

<properties><foo>bar</foo>

</properties>

For a more comprehensive list of available properties, see Chapter 13, Propertiesand Ressource Filterung.

9.4. Project DependenciesMaven can manage both internal and external dependencies. An externaldependency for a Java project might be a library such as Plexus, the SpringFramework, or Log4J. An internal dependency is illustrated by a web applicationproject depending on another project that contains service classes, model objects,or persistence logic. Example 9.3, “Project Dependencies” shows some examplesof project dependencies.

Example 9.3. Project Dependencies

<project>

The Project Object Model

207

Page 227: Maven Definitive Guide De

...<dependencies>

<dependency><groupId>org.codehaus.xfire</groupId><artifactId>xfire-java5</artifactId><version>1.2.5</version>

</dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>3.8.1</version><scope>test</scope>

</dependency><dependency><groupId>javax.servlet</groupId><artifactId>servlet-api</artifactId><version>2.4</version><scope>provided</scope>

</dependency></dependencies>...

</project>

The first dependency is a compile dependency on the XFire SOAP library fromCodehaus. You would use this type of dependency if your project depended on thislibrary for compilation, testing, and during execution. The second dependency is atest-scoped dependency on JUnit. You would use a test-scoped dependencywhen you need to reference this library only during testing. The last dependency inExample 9.3, “Project Dependencies” is a dependency on the Servlet 2.4 API. Thelast dependency is scoped as a provided dependency. You would use a providedscope when the application you are developing needs a library for compilation andtesting, but this library is supplied by a container at runtime.

9.4.1. Dependency ScopeExample 9.3, “Project Dependencies” briefly introduced three of the fivedependency scopes: compile, test, and provided. Scope controls whichdependencies are available in which classpath, and which dependencies areincluded with an application. Let’s explore each scope in detail:

The Project Object Model

208

Page 228: Maven Definitive Guide De

compilecompile is the default scope; all dependencies are compile-scoped if a scope isnot supplied. compile dependencies are available in all classpaths, and they arepackaged.

providedprovided dependencies are used when you expect the JDK or a container toprovide them. For example, if you were developing a web application, youwould need the Servlet API available on the compile classpath to compile aservlet, but you wouldn’t want to include the Servlet API in the packagedWAR; the Servlet API JAR is supplied by your application server or servletcontainer. provided dependencies are available on the compilation classpath(not runtime). They are not transitive, nor are they packaged.

runtimeruntime dependencies are required to execute and test the system, but they arenot required for compilation. For example, you may need a JDBC API JAR atcompile time and the JDBC driver implementation only at runtime.

testtest-scoped dependencies are not required during the normal operation of anapplication, and they are available only during test compilation and executionphases. The test scope was previously introduced in Section 4.10,“Hinzufügen von Gebietsbezogenen Unit Tests”.”

systemThe system scope is similar to provided except that you have to provide anexplicit path to the JAR on the local file system. This is intended to allowcompilation against native objects that may be part of the system libraries. Theartifact is assumed to always be available and is not looked up in a repository. Ifyou declare the scope to be system, you must also provide the systemPath

element. Note that this scope is not recommended (you should always try toreference dependencies in a public or custom Maven repository).

The Project Object Model

209

Page 229: Maven Definitive Guide De

9.4.2. Optional DependenciesAssume that you are working on a library that provides caching behavior. Insteadof writing a caching system from scratch, you want to use some of the existinglibraries that provide caching on the file system and distributed caches. Alsoassume that you want to give the end user an option to cache on the file system orto use an in-memory distributed cache. To cache on the file system, you’ll want touse a freely available library called EHCache (http://ehcache.sourceforge.net/), andto cache in a distributed in-memory cache, you want to use another freely availablecaching library named SwarmCache (http://swarmcache.sourceforge.net/). You’llcode an interface and create a library that can be configured to use either EHCacheor SwarmCache, but you want to avoid adding a dependency on both cachinglibraries to any project that depends on your library.

In other words, you need both libraries to compile this library project, but you don'twant both libraries to show up as transitive runtime dependencies for the projectthat uses your library. You can accomplish this by using optional dependencies asshown in Example 9.4, “Declaring Optional Dependencies”.

Example 9.4. Declaring Optional Dependencies

<project><modelVersion>4.0.0</modelVersion><groupId>org.sonatype.mavenbook</groupId><artifactId>my-project</artifactId><version>1.0.0</version><dependencies>

<dependency><groupId>net.sf.ehcache</groupId><artifactId>ehcache</artifactId><version>1.4.1</version><optional>true</optional>

</dependency><dependency><groupId>swarmcache</groupId><artifactId>swarmcache</artifactId><version>1.0RC2</version><optional>true</optional>

</dependency><dependency><groupId>log4j</groupId><artifactId>log4j</artifactId><version>1.2.13</version>

The Project Object Model

210

Page 230: Maven Definitive Guide De

</dependency></dependencies>

</project>

Once you've declared these dependencies as optional, you are required to includethem explicitly in the project that depends on my-project. For example, if youwere writing an application which depended on my-project and wanted to use theEHCache implementation, you would need to add the following dependency

element to your project.

<project><modelVersion>4.0.0</modelVersion><groupId>org.sonatype.mavenbook</groupId><artifactId>my-application</artifactId><version>1.0.0</version><dependencies>

<dependency><groupId>org.sonatype.mavenbook</groupId><artifactId>my-project</artifactId><version>1.0.0</version>

</dependency><dependency><groupId>net.sf.ehcache</groupId><artifactId>swarmcache</artifactId><version>1.4.1</version>

</dependency></dependencies>

</project>

In an ideal world, you wouldn’t have to use optional dependencies. Instead ofhaving one large project with a series of optional dependencies, you wouldseparate the EHCache-specific code to a my-project-ehcache submodule and theSwarmCache-specific code to a my-project-swarmcache submodule. This way,instead of requiring projects that reference my-project to specifically add adependency, projects can just reference a particular implementation project andbenefit from the transitive dependency.

9.4.3. Dependency Version RangesYou don’t just have to depend on a specific version of a dependency; you canspecify a range of versions that would satisfy a given dependency. For example,

The Project Object Model

211

Page 231: Maven Definitive Guide De

you can specify that your project depends on version 3.8 or greater of JUnit, oranything between versions 1.2.10 and 1.2.14 of JUnit. You do this by surroundingone or more version numbers with the following characters:

(, )Exclusive quantifiers

[, ]Inclusive quantifiers

For example, if you wished to access any JUnit version greater than or equal to 3.8but less than 4.0, your dependency would be as shown in Example 9.5, “Specifyinga Dependency Range: JUnit 3.8 - JUnit 4.0”.

Example 9.5. Specifying a Dependency Range: JUnit 3.8 - JUnit 4.0

<dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>[3.8,4.0)</version><scope>test</scope>

</dependency>

If you want to depend on any version of JUnit no higher than 3.8.1, you wouldspecify only an upper inclusive boundary, as shown in Example 9.6, “Specifying aDependency Range: JUnit <= 3.8.1”.

Example 9.6. Specifying a Dependency Range: JUnit <= 3.8.1

<dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>[,3.8.1]</version>ex-de<scope>test</scope>

</dependency>

A version before or after the comma is not required, and means +/- infinity. Forexample, "[4.0,)" means any version greater than or equal to 4.0. "(,2.0)" is any

The Project Object Model

212

Page 232: Maven Definitive Guide De

version less than 2.0. "[1.2]" means only version 1.2, and nothing else.

NoteWhen declaring a "normal" version such as 3.8.2 for Junit, internally thisis represented as "allow anything, but prefer 3.8.2." This means that whena conflict is detected, Maven is allowed to use the conflict algorithms tochoose the best version. If you specify [3.8.2], it means that only 3.8.2will be used and nothing else. If somewhere else there is a dependencythat specifies [3.8.1], you would get a build failure telling you of theconflict. We point this out to make you aware of the option, but use itsparingly and only when really needed. The preferred way to resolve thisis via dependencyManagement.

9.4.4. Transitive DependenciesA transitive dependency is a dependency of a dependency. If project-a dependson project-b, which in turn depends on project-c, then project-c is considereda transitive dependency of project-a. If project-c depended on project-d, thenproject-d would also be considered a transitive dependency of project-a. Part ofMaven’s appeal is that it can manage transitive dependencies and shield thedeveloper from having to keep track of all of the dependencies required to compileand run an application. You can just depend on something like the SpringFramework and not have to worry about tracking down every last dependency ofthe Spring Framework.

Maven accomplishes this by building a graph of dependencies and dealing withany conflicts and overlaps that might occur. For example, if Maven sees that twoprojects depend on the same groupId and artifactId, it will sort out whichdependency to use automatically, always favoring the more recent version of adependency. Although this sounds convenient, there are some edge cases wheretransitive dependencies can cause some configuration issues. For these scenarios,you can use a dependency exclusion.

The Project Object Model

213

Page 233: Maven Definitive Guide De

9.4.4.1. Transitive Dependencies and ScopeEach of the scopes outlined earlier in the section Section 9.4.1, “DependencyScope”” affects not just the scope of the dependency in the declaring project, butalso how it acts as a transitive dependency. The easiest way to convey thisinformation is through a table, as in Table 9.1, “How Scope Affects TransitiveDependencies”. Scopes in the top row represent the scope of a transitivedependency. Scopes in the leftmost column represent the scope of a directdependency. The intersection of the row and column is the scope that is assigned toa transitive dependency. A blank cell in this table means that the transitivedependency will be omitted.

Table 9.1. How Scope Affects Transitive Dependencies

Direct Scope TransitiveScope

compile provided runtime test

compile compile - runtime -

provided provided provided provided -

runtime runtime - runtime -

test test - test -

To illustrate the relationship of transitive dependency scope to direct dependencyscope, consider the following example. If project-a contains a test scopeddependency on project-b which contains a compile scoped dependency onproject-c. project-c would be a test-scoped transitive dependency of project-a.

You can think of this as a transitive boundary which acts as a filter on dependencyscope. Transitive dependencies which are provided and test scope usually do notaffect a project. The exception to this rule is that a provided scoped transitivedependency to a provided scope direct dependency is still a provided dependencyof a project. Transitive dependencies which are compile and runtime scoped

The Project Object Model

214

Page 234: Maven Definitive Guide De

usually affect a project regardless of the scope of a direct dependency. Transitivedependencies which are compile scoped will have the same scope irregardless ofthe scope of the direct dependency. Transitive dependencies which are runtimescoped will generally have the same scope of the direct dependency except whenthe direct dependency has a scope of compile. When a transitive dependency isruntime scoped and a direct is compile scoped the direct dependency the transitivedependency will have an effective scope of runtime.

9.4.5. Conflict ResolutionThere will be times when you need to exclude a transitive dependency, such aswhen you are depending on a project that depends on another project, but youwould like to either exclude the dependency altogether or replace the transitivedependency with another dependency that provides the same functionality.Example 9.7, “Excluding a Transitive Dependency” shows an example of adependency element that adds a dependency on project-a, but excludes thetransitive dependency project-b.

Example 9.7. Excluding a Transitive Dependency

<dependency><groupId>org.sonatype.mavenbook</groupId><artifactId>project-a</artifactId><version>1.0</version><exclusions>

<exclusion><groupId>org.sonatype.mavenbook</groupId><artifactId>project-b</artifactId>

</exclusion></exclusions>

</dependency>

Often, you will want to replace a transitive dependency with anotherimplementation. For example, if you are depending on a library that depends on theSun JTA API, you may want to replace the declared transitive dependency.Hibernate is one example. Hibernate depends on the Sun JTA API JAR, which isnot available in the central Maven repository because it cannot be freely

The Project Object Model

215

Page 235: Maven Definitive Guide De

redistributed. Fortunately, the Apache Geronimo project has created anindependent implementation of this library that can be freely redistributed. Toreplace a transitive dependency with another dependency, you would exclude thetransitive dependency and declare a dependency on the project you wanted instead.Example 9.8, “Excluding and Replacing a Transitive Dependency” shows anexample of a such replacement.

Example 9.8. Excluding and Replacing a Transitive Dependency

<dependencies><dependency>

<groupId>org.hibernate</groupId><artifactId>hibernate</artifactId><version>3.2.5.ga</version><exclusions><exclusion>

<groupId>javax.transaction</groupId><artifactId>jta</artifactId>

</exclusion></exclusions>

</dependency><dependency>

<groupId>org.apache.geronimo.specs</groupId><artifactId>geronimo-jta_1.1_spec</artifactId><version>1.1</version>

</dependency></dependencies>

In Example 9.8, “Excluding and Replacing a Transitive Dependency”, there isnothing marking the dependency on geronimo-jta_1.1_spec as a replacement, itjust happens to be a library which provides the same API as the original JTAdependency. Here are some other reasons you might want to exclude or replacetransitive dependencies:

1. The groupId or artifactId of the artifact has changed, where the currentproject requires an alternately named version from a dependency's version -resulting in 2 copies of the same project in the classpath. Normally Mavenwould capture this conflict and use a single version of the project, but whengroupId or artifactId are different, Maven will consider this to be twodifferent libraries.

The Project Object Model

216

Page 236: Maven Definitive Guide De

2. An artifact is not used in your project and the transitive dependency has notbeen marked as an optional dependency. In this case, you might want toexclude a dependency because it isn't something your system needs and youare trying to cut down on the number of libraries distributed with anapplication.

3. An artifact which is provided by your runtime container thus should not beincluded with your build. An example of this is if a dependency depends onsomething like the Servlet API and you want to make sure that thedependency is not included in a web application's WEB-INF/lib directory.

4. To exclude a dependency which might be an API with multipleimplementations. This is the situation illustrated by Example 9.8,“Excluding and Replacing a Transitive Dependency”; there is a Sun APIwhich requires click-wrap licensing and a time-consuming manual installinto a custom repository (Sun's JTA JAR) versus a freely distributed versionof the same API available in the central Maven repository (Geronimo's JTAimplementation).

9.4.6. Dependency ManagementOnce you've adopted Maven at your super complex enterprise and you have twohundred and twenty inter-related Maven projects, you are going to start wonderingif there is a better way to get a handle on dependency versions. If every singleproject that uses a dependency like the MySQL Java connector needs toindependently list the version number of the dependency, you are going to run intoproblems when you need to upgrade to a new version. Because the versionnumbers are distributed throughout your project tree, you are going to have tomanually edit each of the pom.xml files that reference a dependency to make surethat you are changing the version number everywhere. Even with find, xargs, andawk, you are still running the risk of missing a single POM.

Luckily, Maven provides a way for you to consolidate dependency versionnumbers in the dependencyManagement element. You'll usually see the

The Project Object Model

217

Page 237: Maven Definitive Guide De

dependencyManagement element in a top-level parent POM for an organization orproject. Using the dependencyManagement element in a pom.xml allows you toreference a dependency in a child project without having to explicitly list theversion. Maven will walk up the parent-child hierarchy until it finds a project witha dependencyManagement element, it will then use the version specified in thisdependencyManagement element.

For example, if you have a large set of projects which make use of the MySQLJava connector version 5.1.2, you could define the followingdependencyManagement element in your multi-module project's top-level POM.

Example 9.9. Defining Dependency Versions in a Top-level POM

<project><modelVersion>4.0.0</modelVersion><groupId>org.sonatype.mavenbook</groupId><artifactId>a-parent</artifactId><version>1.0.0</version>...<dependencyManagement>

<dependencies><dependency>

<groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.2</version>

</dependency>...

<dependencies></dependencyManagement>

Then, in a child project, you can add a dependency to the MySQL Java Connectorusing the following dependency XML:

<project><modelVersion>4.0.0</modelVersion><parent>

<groupId>org.sonatype.mavenbook</groupId><artifactId>a-parent</artifactId><version>1.0.0</version>

</parent><artifactId>project-a</artifactId>...<dependencies>

<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId>

The Project Object Model

218

Page 238: Maven Definitive Guide De

</dependency></dependencies>

</project>

You should notice that the child project did not have to explicitly list the version ofthe mysql-connector-java dependency. Because this dependency was defined inthe top-level POM's dependencyManagement element, the version number is goingto propagate to the child project's dependency on mysql-connector-java. Notethat if this child project did define a version, it would override the version listed inthe top-level POM's dependencyManagement section. That is, thedependencyManagement version is only used when the child does not declare aversion directly.

Dependency management in a top-level POM is different from just defining adependency on a widely shared parent POM. For starters, all dependencies areinherited. If mysql-connector-java were listed as a dependency of the top-levelparent project, every single project in the hierarchy would have a reference to thisdependency. Instead of adding in unnecessary dependencies, usingdependencyManagement allows you to consolidate and centralize the managementof dependency versions without adding dependencies which are inherited by allchildren. In other words, the dependencyManagement element is equivalent to anenvironment variable which allows you to declare a dependency anywhere below aproject without specifying a version number.

9.5. Project RelationshipsOne of the compelling reasons to use Maven is that it makes the process oftracking down dependencies (and dependencies of dependencies) very easy. Whena project depends on an artifact produced by another project we say that thisartifact is a dependency. In the case of a Java project, this can be as simple as aproject depending on an external dependency like Log4J or JUnit. Whiledependencies can model external dependencies, they can also manage thedependencies between a set of related projects, if project-a depends onproject-b, Maven is smart enough to know that project-b must be built before

The Project Object Model

219

Page 239: Maven Definitive Guide De

project-a.

Relationships are not only about dependencies and figuring out what one projectneeds to be able to build an artifact. Maven can model the relationship of a projectto a parent, and the relationship of a project to submodules. This section gives anoverview of the various relationships between projects and how such relationshipsare configured.

9.5.1. More on CoordinatesCoordinates define a unique location for a project, they were first introduced inChapter 3, Ein einfaches Maven Projekt. Projects are related to one another usingMaven Coordinates. project-a doesn't just depend on project-b; a project with agroupId, artifactId, and version depends on another project with a groupId,artifactId, and version. To review, a Maven Coordinate is made up of threecomponents:

groupIdA groupId groups a set of related artifacts. Group identifiers generally resemblea Java package name. For example, the groupId org.apache.maven is the basegroupId for all artifacts produced by the Apache Maven project. Groupidentifiers are translated into paths in the Maven Repository; for example, theorg.apache.maven groupId can be found in /maven2/org/apache/maven onrepo1.maven.org.

artifactIdThe artifactId is the project's main identifier. When you generate an artifact,this artifact is going to be named with the artifactId. When you refer to aproject, you are going to refer to it using the artifactId. The artifactId,groupId combination must be unique. In other words, you can't have twoseparate projects with the same artifactId and groupId; artifactIds areunique within a particular groupId.

NoteWhile '.'s are commonly used in groupIds, you should try to avoid

The Project Object Model

220

Page 240: Maven Definitive Guide De

using them in artifactIds. This can cause issues when trying toparse a fully qualified name down into the subcomponents.

versionWhen an artifact is released, it is released with a version number. This versionnumber is a numeric identifier such as "1.0", "1.1.1", or "1.1.2-alpha-01". Youcan also use what is known as a snapshot version. A snapshot version is aversion for a component which is under development, snapshot versionnumbers always end in SNAPSHOT; for example, "1.0-SNAPSHOT","1.1.1-SNAPSHOT", and "1-SNAPSHOT". Section 9.3.1.1, “Version BuildNumbers” introduces versions and version ranges.

There is a fourth, less-used qualifier:

classifierYou would use a classifier if you were releasing the same code but needed toproduce two separate artifacts for technical reasons. For example, if you wantedto build two separate artifacts of a JAR, one compiled with the Java 1.4compiler and another compiled with the Java 6 compiler, you might use theclassifier to produce two separate JAR artifacts under the samegroupId:artifactId:version combination. If your project uses native extensions,you might use the classifier to produce an artifact for each target platform.Classifiers are commonly used to package up an artifact's sources, JavaDocs orbinary assemblies.

When we talk of dependencies in this book, we often use the following shorthandnotation to describe a dependency: groupId:artifactId:version. To refer to the2.5 release of the Spring Framework, we would refer to it asorg.springframework:spring:2.5. When you ask Maven to print out a list ofdependencies with the Maven Dependency plugin, you will also see that Maventends to print out log messages with this shorthand dependency notation.

9.5.2. Multi-module Projects

The Project Object Model

221

Page 241: Maven Definitive Guide De

Multi-module projects are projects which contain a list of modules to build. Amulti-module project always has a packaging of pom, and rarely produces anartifact. A multi-module project exists only to group projects together in a build.Figure 9.3, “Multi-module Project Relationships” shows a project hierarchy whichincludes two parent projects with packaging of pom, and three projects withpackaging of jar.

Figure 9.3. Multi-module Project Relationships

The directory structure on the file system would also mirror the modulerelationships. A set of projects illustrated by Figure 9.3, “Multi-module ProjectRelationships” would have the following directory structure:

top-group/pom.xmltop-group/sub-group/pom.xmltop-group/sub-group/project-a/pom.xmltop-group/sub-group/project-b/pom.xmltop-group/project-c/pom.xml

The projects are related to one another because top-group and sub-group arereferencing sub-modules in a POM. For example, theorg.sonatype.mavenbook:top-group project is a multi-module project with

The Project Object Model

222

Page 242: Maven Definitive Guide De

packaging of type pom. top-group's pom.xml would include the following moduleselement:

Example 9.10. top-group modules element

<project><groupId>org.sonatype.mavenbook</groupId><artifactId>top-group</artifactId>...<modules>

<module>sub-group</module><module>project-c</module>

</modules>...

</project>

When Maven is reading top-group POM it will look at the modules element andsee that top-group references the projects sub-group and project-c. Maven willthen look for a pom.xml in each of these subdirectories. Maven repeats this processfor each of the submodules: it will read the sub-group/pom.xml and see that thesub-group project references two projects with the following modules element:

Example 9.11. sub-group modules element

<project>...<modules>

<module>project-a</module><module>project-b</module>

</modules>...

</project>

Note that we call the projects under the multi-module projects "modules" and not"children" or "child projects". This is purposeful, so as not to confuse projectsgrouped by multi-module projects with projects that inherit POM information fromeach other.

9.5.3. Project Inheritance

The Project Object Model

223

Page 243: Maven Definitive Guide De

There are going to be times when you want a project to inherit values from a parentPOM. You might be building a large system, and you don't want to have to repeatthe same dependency elements over and over again. You can avoid repeatingyourself if your projects make use of inheritance via the parent element. When aproject specifies a parent, it inherits the information in the parent project's POM. Itcan then override and add to the values specified in this parent POM.

All Maven POMs inherit values from a parent POM. If a POM does not specify adirect parent using the parent element, that POM will inherit values from theSuper POM. Example 9.12, “Project Inheritance” shows the parent element ofproject-a which inherits the POM defined by the a-parent project.

Example 9.12. Project Inheritance

<project><parent>

<groupId>com.training.killerapp</groupId><artifactId>a-parent</artifactId><version>1.0-SNAPSHOT</version>

</parent><artifactId>project-a</artifactId>...

</project>

Running mvn help:effective-pom in project-a would show a POM that is theresult of merging the Super POM with the POM defined by a-parent and thePOM defined in project-a. The implicit and explicit inheritance relationships forproject-a are shown in Figure 9.4, “Project Inheritance for a-parent andproject-a”.

The Project Object Model

224

Page 244: Maven Definitive Guide De

Figure 9.4. Project Inheritance for a-parent and project-a

When a project specifies a parent project, Maven uses that parent POM as astarting point before it reads the current project's POM. It inherits everything,including the groupId and version number. You'll notice that project-a does notspecify either, both groupId and version are inherited from a-parent. With aparent element, all a POM really needs to define is an artifactId. This isn'tmandatory, project-a could have a different groupId and version, but by notproviding values, Maven will use the values specified in the parent POM. If youstart using Maven to manage and build large multi-module projects, you will oftenbe creating many projects which share a common groupId and version.

When you inherit a POM, you can choose to live with the inherited POMinformation or to selectively override it. The following is a list of items a MavenPOM inherits from its parent POM:

• identifiers (at least one of groupId or artifactId must be overridden.)

• dependencies

The Project Object Model

225

Page 245: Maven Definitive Guide De

• developers and contributors

• plugin lists

• reports lists

• plugin executions (executions with matching ids are merged)

• plugin configurationWhen Maven inherits dependencies, it will add dependencies of child projects tothe dependencies defined in parent projects. You can use this feature of Maven tospecify widely used dependencies across all projects which inherit from a top-levelPOM. For example, if your system makes universal use of the Log4J loggingframework, you can list this dependency in your top-level POM. Any projectswhich inherit POM information from this project will automatically have Log4J asa dependency. Similarly, if you need to make sure that every project is using thesame version of a Maven plugin, you can list this Maven plugin version explicitlyin a top-level parent POM's pluginManagement section.

Maven assumes that the parent POM is available from the local repository, oravailable in the parent directory (../pom.xml) of the current project. If neitherlocation is valid this default behavior may be overridden via the relativePath

element. For example, some organizations prefer a flat project structure where aparent project's pom.xml isn't in the parent directory of a child project. It might bein a sibling directory to the project. If your child project were in a directory./project-a and the parent project were in a directory named ./a-parent, youcould specify the relative location of parent-a's POM with the followingconfiguration:

<project><parent>

<groupId>org.sonatype.mavenbook</groupId><artifactId>a-parent</artifactId><version>1.0-SNAPSHOT</version><relativePath>../a-parent/pom.xml</relativePath>

</parent><artifactId>project-a</artifactId>

</project>

The Project Object Model

226

Page 246: Maven Definitive Guide De

9.6. POM Best PracticesMaven can be used to manage everything from simple, single-project systems tobuilds that involve hundreds of inter-related submodules. Part of the learningprocess with Maven isn't just figuring out the syntax for configuring Maven, it islearning the "Maven Way"—the current set of best practices for organizing andbuilding projects using Maven. This section attempts to distill some of thisknowledge to help you adopt best practices from the start without having to wadethrough years of discussions on the Maven mailing lists.

9.6.1. Grouping DependenciesIf you have a set of dependencies which are logically grouped together. You cancreate a project with pom packaging that groups dependencies together. Forexample, let's assume that your application uses Hibernate, a popularObject-Relational mapping framework. Every project which uses Hibernate mightalso have a dependency on the Spring Framework and a MySQL JDBC driver.Instead of having to include these dependencies in every project that usesHibernate, Spring, and MySQL you could create a special POM that does nothingmore than declare a set of common dependencies. You could create a project calledpersistence-deps (short for Persistence Dependencies), and have every projectthat needs to do persistence depend on this convenience project:

Example 9.13. Consolidating Dependencies in a Single POM Project

<project><groupId>org.sonatype.mavenbook</groupId><artifactId>persistence-deps</artifactId><version>1.0</version><packaging>pom</packaging><dependencies>

<dependency><groupId>org.hibernate</groupId><artifactId>hibernate</artifactId><version>${hibernateVersion}</version>

</dependency><dependency><groupId>org.hibernate</groupId><artifactId>hibernate-annotations</artifactId>

The Project Object Model

227

Page 247: Maven Definitive Guide De

<version>${hibernateAnnotationsVersion}</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-hibernate3</artifactId><version>${springVersion}</version>

</dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>${mysqlVersion}</version>

</dependency></dependencies><properties>

<mysqlVersion>(5.1,)</mysqlVersion><springVersion>(2.0.6,)</springVersion><hibernateVersion>3.2.5.ga</hibernateVersion><hibernateAnnotationsVersion>3.3.0.ga</hibernateAnnotationsVersion>

</properties></project>

If you create this project in a directory named persistence-deps, all you need todo is create this pom.xml and run mvn install. Since the packaging type is pom, thisPOM is installed in your local repository. You can now add this project as adependency and all of its dependencies will be added to your project. When youdeclare a dependency on this persistence-deps project, don't forget to specify thedependency type as pom.

Example 9.14. Declaring a Dependency on a POM

<project><description>This is a project requiring JDBC</description>...<dependencies>

...<dependency><groupId>org.sonatype.mavenbook</groupId><artifactId>persistence-deps</artifactId><version>1.0</version><type>pom</type>

</dependency></dependencies>

</project>

The Project Object Model

228

Page 248: Maven Definitive Guide De

If you later decide to switch to a different JDBC driver (for example, JTDS), justreplace the dependencies in the persistence-deps project to usenet.sourceforge.jtds:jtds instead of mysql:mysql-java-connector andupdate the version number. All projects depending on persistence-deps will useJTDS if they decide to update to the newer version. Consolidating relateddependencies is a good way to cut down on the length of pom.xml files that starthaving to depend on a large number of dependencies. If you need to share a largenumber of dependencies between projects, you could also just establishparent-child relationships between projects and refactor all common dependenciesto the parent project, but the disadvantage of the parent-child approach is that aproject can have only one parent. Sometimes it makes more sense to group similardependencies together and reference a pom dependency. This way, your project canreference as many of these consolidated dependency POMs as it needs.

NoteMaven uses the depth of a dependency in the tree when resolvingconflicts using a nearest-wins approach. Using the dependency groupingtechnique above pushes those dependencies one level down in the tree.Keep this in mind when choosing between grouping in a pom or usingdependenctManagement in a parent POM

9.6.2. Multi-module vs. InheritanceThere is a difference between inheriting from a parent project and being managedby a multimodule project. A parent project is one that passes its values to itschildren. A multimodule project simply manages a group of other subprojects ormodules. The multimodule relationship is defined from the topmost leveldownwards. When setting up a multimodule project, you are simply telling aproject that its build should include the specified modules. Multimodule builds areto be used to group modules together in a single build. The parent-childrelationship is defined from the leaf node upwards. The parent-child relationshipdeals more with the definition of a particular project. When you associate a childwith its parent, you are telling Maven that a project’s POM is derived from

The Project Object Model

229

Page 249: Maven Definitive Guide De

another.

To illustrate the decision process that goes into choosing a design that usesinheritance vs. multi-module or both approaches consider the following twoexamples: the Maven project used to generate this book and a hypothetical projectthat contains a number of logically grouped modules.

9.6.2.1. Simple ProjectFirst, let's take a look at the maven-book project. The inheritance and multi-modulerelationships are shown in Figure 9.5, “maven-book Multi-module vs. Inheritance”.

Figure 9.5. maven-book Multi-module vs. Inheritance

When we build this Maven book you are reading, we run mvn package in amulti-module project named maven-book. This multi-module project includes twosubmodules: book-examples and book-chapters. Neither of these projects sharethe same parent, they are related only in that they are modules in the maven-book

The Project Object Model

230

Page 250: Maven Definitive Guide De

project. book-examples builds the ZIP and TGZ archives you downloaded to getthis book's example. When we run the book-examples build from book-examples/

directory with mvn package, it has no knowledge that it is a part of the largermaven-book project. book-examples doesn't really care about maven-book, all itknows in life is that its parent is the top-most sonatype POM and that it creates anarchive of examples. In this case, the maven-book project exists only as aconvenience and as a aggregator of modules.

The book projects do all define a parent. Each of the three projects: maven-book,book-examples, and book-chapters all list a shared "corporate" parent —sonatype. This is a common practice in organizations which have adopted Maven,instead of having every project extend the Super POM by default, someorganizations define a top-level corporate POM that serves as the default parentwhen a project doesn't have any good reason to depend on another. In this bookexample, there is no compelling reason to have book-examples andbook-chapters share the same parent POM, they are entirely different projectswhich have a different set of dependencies, a different build configuration, and usedrastically different plugins to create the content you are now reading. Thesonatype POM gives the organization a change to customize the default behaviorof Maven and supply some organization-specific information to configuredeployment settings and build profiles.

9.6.2.2. Multi-module Enterprise ProjectLet's take a look at an example that provides a more accurate picture of areal-world project where inheritance and multi-module relationships exist side byside. Figure 9.6, “Enterprise Multi-module vs. Inheritance” shows a collection ofprojects that resemble a typical set of projects in an enterprise application. There isa top-level POM for the corporation with an artifactId of sonatype. There is amulti-module project named big-system which references sub-modulesserver-side and client-side.

The Project Object Model

231

Page 251: Maven Definitive Guide De

Figure 9.6. Enterprise Multi-module vs. Inheritance

What's going on here? Let's try to deconstruct this confusing set of arrows. First,let's take a look at big-system. The big-system might be the project that youwould run mvn package on to build and test the entire system. big-systemreferences submodules client-side and server-side. Each of these projectseffectively rolls up all of the code that runs on either the server or on the client.Let's focus on the server-side project. Under the server-side project we have aproject called server-lib and a multi-module project named web-apps. Underweb-apps we have two Java web applications: client-web and admin-web.

Let's start with the parent/child relationships from client-web and admin-web toweb-apps. Since both of the web applications are implemented in the same web

The Project Object Model

232

Page 252: Maven Definitive Guide De

application framework (let's say Wicket), both projects would share the same set ofcore dependencies. The dependencies on the Servlet API, the JSP API, and Wicketwould all be captured in the web-apps project. Both client-web and admin-web

also need to depend on server-lib, this dependency would be defined as adependency between web-apps and server-lib. Because client-web andadmin-web share so much configuration by inheriting from web-apps, bothclient-web and admin-web will have very small POMs containing little more thanidentifiers, a parent declaration, and a final build name.

Next we focus on the parent/child relationship from web-apps and server-lib toserver-side. In this case, let's just assume that there is a separate working groupof developers which work on the server-side code and another group of developersthat work on the client-side code. The list of developers would be configured in theserver-side POM and inherited by all of the child projects underneath it:web-apps, server-lib, client-web, and admin-web. We could also imagine thatthe server-side project might have different build and deployment settings whichare unique to the development for the server side. The server-side project mightdefine a build profile that only makes sense for all of the server-side projects.This build profile might contain the database host and credentials, or theserver-side project's POM might configure a specific version of the Maven Jettyplugin which should be universal across all projects that inherit the server-side

POM.

In this example, the main reason to use parent/child relationships is shareddependencies and common configuration for a group of projects which arelogically related. All of the projects below big-system are related to one anotheras submodules, but not all submodules are configured to point back to parentproject that included it as a submodule. Everything is a submodule for reasons ofconvenience, to build the entire system just go to the big-system project directoryand run mvn package. Look more closely at the figure and you'll see that there isno parent/child relationship between server-side and big-system. Why is this?POM inheritance is very powerful, but it can be overused. When it makes sense toshare dependencies and build configuration, a parent/child relationship should beused. When it doesn't make sense is when there are distinct differences betweentwo projects. Take, for example, the server-side and client-side projects. It is

The Project Object Model

233

Page 253: Maven Definitive Guide De

possible to create a system where client-side and server-side inherited acommon POM from big-system, but as soon as a significant divergence betweenthe two child projects develops, you then have to figure out creative ways to factorout common build configuration to big-system without affecting all of thechildren. Even though client-side and server-side might both depend onLog4J, they also might have distinct plugin configurations.

There's a certain point defined more by style and experience where you decide thatminimal duplication of configuration is a small price to pay for allowing projectslike client-side and server-side to remain completely independent. Designinga huge set of thirty plus projects which all inherit five levels of POM configurationisn't always the best idea. In such a setup, you might not have to duplicate yourLog4J dependency more than once, but you'll also end up having to wade throughfive levels of POM just figure out how Maven calculated your effective POM. Allof this complexity to avoid duplicating five lines of dependency declaration. InMaven, there is a "Maven Way", but there are also many ways to accomplish thesame thing. It all boils down to preference and style. For the most part, you won'tgo wrong if all of your submodules turn out to define back-references to the sameproject as a parent, but your use of Maven may evolve over time.

9.6.2.3. Prototype Parent ProjectsTake the following example shown in Figure 9.7, “Using parent projects as"prototypes" for specialized projects” as another hypothetical and creative way touse inheritance and multi-modules builds to reuse dependencies.

The Project Object Model

234

Page 254: Maven Definitive Guide De

Figure 9.7. Using parent projects as "prototypes" for specialized projects

Figure 9.7, “Using parent projects as "prototypes" for specialized projects” is yetanother way to think about inheritance and multi-module projects. In this example,you have two distinct systems. system-a and system-b each define independentapplications. system-a defines two modules a-lib and a-swing. system-a anda-lib both define the top-level sonatype POM as a parent project, but the a-swing

project defines swing-proto as a parent project. In this system, swing-protosupplies a foundational POM for Swing applications and the struts-proto projectprovides a foundational POM for Struts 2 web applications. While the sonatype

POM provides high level information such as the groupId, organizationinformation, and build profiles, struts-proto defines all of the dependencies thatyou need to create a struts application. This approach would work well if your

The Project Object Model

235

Page 255: Maven Definitive Guide De

development is characterized by many independent applications which each haveto follow the same set of rules. If you are creating a lot of struts applications butthey are not really related to one another, you might just define everything youneed in struts-proto. The downside to this approach is that you won't be able touse parent/child relationships within the system-a and system-b projecthierarchies to share information like developers and other build configuration. Aproject can only have one parent.

The other downside of this approach is that as soon as you have one project that"breaks the mold" you'll either have to override the prototype parent POM or find away to factor customizations into the shared parent without those customizationsaffecting all the children. In general, using POMs as prototypes for specializedproject "types" isn't a recommended practice.

The Project Object Model

236

Page 256: Maven Definitive Guide De

Chapter 10. Der Build Lebenszyklus

10.1. EinführungMaven modelliert Projekte als Nomen welche von einem Projekt Objekt Modell(POM) beschrieben werden. Das POM fängt die Identität des Projektes ein: Wasbeinhaltet das Projekt? Welche Art der Packetierung benötigt das Projekt? Ist dasProjekt ein abgeleitetes Projekt? Was sind die Abhängigkeiten? Imvorangegangenen Kapitel haben wir eingeführt wie ein Projekt beschrieben werdenkann, was wir nicht eingeführt haben sind die Mechanismen mittels welchenMaven auf diesen Objekten agieren kann. Im Maven Universum werden Verbendurch Goals, welche in Maven Plugins verpackt und an Lebenszyklusphasengebunden sind, dargestellt. Ein Maven Lebenszyklus besteht aus einer Reihenamentlich benannter Lebenszyklusphasen: prepare-resources, compile,package und install neben anderen. Es gibt also eine Phase welche dieKompilierung beschreibt und eine Phase welche die Packetierung umfasst. Darüberhinaus gibt es vorbereitende und nachbereitende Phasen, welche benutzt werden,um Goals welche vor oder gerade nach einer bestimmten Phase abgearbeitetwerden müssen zu registrieren. Sobald Sie Maven aufrufen um einen Build zuerstellen, rufen Sie tatsächlich Maven dazu auf eine bestimmte Kette von Phasenzu durchlaufen und alle an eine Phase gebndene Goals abzuarbeiten.

Ein Build Lebenszyklus ist eine Abfolge von Phasen welche existiert, um einerAnzahl Goals zu ordnen. Diese Goals sind gewählt und an Phasen gebundenabhängig vom entsprechenden Packetierungstyp des Projektes in Verarbeitung. InMaven gibt es drei Standard Lebenszyklen: clean, default (auch build genannt)und site. Dieses Kapitel wird erläutern, wie Maven Goals an Lebenszyklusphasenbindet und wie Lebenszyklen angepasst werden können. Die StandardLebenszyklusphasen werden ebenfalls eingeführt.

10.1.1. Lebenszyklus: cleanDer erste Lebenszyklus für welchen Sie sich interessieren werden ist zugleich der

237

Page 257: Maven Definitive Guide De

einfachste Lebenszyklus von Maven. Der Aufruf von mvn clean started denLebenszyklus clean welcher aus drei Phasen besteht:

• pre-clean

• clean

• post-clean

Die interessante Phase des Lebenszyklus clean ist die Phase clean. Das Goalclean (clean:clean) des Plugins clean wird an die Phase clean des Lebenszyklusclean gebunden. (Noch ganz sauber oder was!?!) Das Goal clean:clean löschtdie Ergebnisse des vorgehenden Builds indem es das Zielverzeichnis löscht.Sollten Sie das Zielverzeichnis nicht weiter angepasst haben, so ist diesesZielverzeichnis das Verzeichnis ${basedir}/target wie dies im Super POMdefiniert ist. Die Ausführung des Goals clean:clean wird nicht direkt mit demAufruf mvn clean:clean gestartet, statt dessen wird die Phase clean desLebenszyklus zur Ausführung gebracht. Die Ausführung der Phase clean

ermöglicht Maven auch Goals welche an die Phase pre-clean gebunden sindauszuführen.

Ein Beispiel: nehmen wir an Sie möchten das Goal antrun:run während der Phasepre-clean anstossen, um eine Nachricht auszugeben, oder um ein Archiv desProject Build Verzeichnis anzulegen bevor dieses gelöscht wird. Der Aufruf vonclean:clean wird den Lebenszyklus gar nicht ausführen, jedoch wird die Angabeder Phase clean auslösen, dass die drei Lebenszyklusphasen bis zur Phase clean

ausgeführt werden. Example 10.1, “Auslösen eines Goals in der Phase pre-clean”(Beispiel 10.1: Auslösen eines Goals in der Phase pre-clean) zeigt beispielhaft eineBuild Konfiguration welche das Goal antrun:run and die Phase pre-clean bindetum eine Warnung auszugeben, welche mitteilt, dass der Projektartefakt gelöschtwerden soll. In diesem Beispiel wird das Goal antrun:run benutzt, um einbeliebiges Ant Kommando auszuführen um zu prüfen ob es den Artefakten gibt.Besteht der Artefakt und soll gelöscht werden, so wird dies auf der Konsoleausgegeben.

Example 10.1. Auslösen eines Goals in der Phase pre-clean

Der Build Lebenszyklus

238

Page 258: Maven Definitive Guide De

<project>...<build>

<plugins>... <plugin><artifactId>maven-antrun-plugin</artifactId><executions><execution>

<id>file-exists</id><phase>pre-clean</phase><goals>

<goal>run</goal></goals><configuration>

<tasks><!-- adds the ant-contrib tasks (if/then/else used below) --><taskdef resource="net/sf/antcontrib/antcontrib.properties" /><available

file="${project.build.directory}/${project.build.finalName}.${project.packaging}"property="file.exists" value="true" />

<if><not><isset property="file.exists" />

</not><then><echo>No${project.build.finalName}.${project.packaging} todelete</echo>

</then><else><echo>Deleting${project.build.finalName}.${project.packaging}</echo>

</else></if>

</tasks></configuration>

</execution></executions><dependencies><dependency>

<groupId>ant-contrib</groupId><artifactId>ant-contrib</artifactId><version>1.0b2</version>

</dependency></dependencies>

</plugin></plugins></build>

</project>

Das Ausführen von mvn clean auf einem Projekt mit der oben gegebenen Build

Der Build Lebenszyklus

239

Page 259: Maven Definitive Guide De

Konfiguration wird eine Ausgabe ähnlich der unten wiedergegebenen erzeugen.

[INFO] Scanning for projects...[INFO] ----------------------------------------------------------------------[INFO] Building Your Project[INFO] task-segment: [clean][INFO] ----------------------------------------------------------------------[INFO] [antrun:run {execution: file-exists}][INFO] Executing tasks

[echo] Deleting your-project-1.0-SNAPSHOT.jar[INFO] Executed tasks[INFO] [clean:clean][INFO] Deleting directory ~/corp/your-project/target[INFO] Deleting directory ~/corp/your-project/target/classes[INFO] Deleting directory ~/corp/your-project/target/test-classes[INFO] ------------------------------------------------------------------------[INFO] BUILD SUCCESSFUL[INFO] ------------------------------------------------------------------------[INFO] Total time: 1 second[INFO] Finished at: Wed Nov 08 11:46:26 CST 2006[INFO] Final Memory: 2M/5M[INFO] ------------------------------------------------------------------------

Zusätzlich zur Möglichkeit Maven so zu konfigurieren, dass es ein Goal währendder Phase pre-clean ausführt wird, kann man das Clean Plugin so anzupassen,dass es Dateien über denen des Zielverzeichnisses hinaus löscht. Sie können dasPlugin so konfigurieren, dass es bestimmte in einer Aufzählung angegebeneDateien löscht. Das unten aufgeführte Beispiel konfiguriert clean so, dass es unterEinsatz der standard Ant Wildcards (* und **) alle .class Dateien in einemVerzeichnis mit dem Namen target-other/ löscht

Example 10.2. Anpassen des Verhaltens des Clean Plugin

<project><modelVersion>4.0.0</modelVersion>...<build>

<plugins><plugin>

<artifactId>maven-clean-plugin</artifactId><configuration>

<filesets><fileset>

<directory>target-other</directory><includes><include>*.class</include>

Der Build Lebenszyklus

240

Page 260: Maven Definitive Guide De

</includes></fileset>

</filesets></configuration>

</plugin></plugins>

</build></project>

10.1.2. Standard Lebenszyklus: defaultDie meisten Maven Benutzer sind mit dem Standardlebenszyklus vertraut. Dieserbildet das algemeine Modell des Build Prozesses einer Software Applikation ab.Die erste Phase ist validate und die letzte Phase deploy. Die Phasen des MavenStandard Lebenszyklus sind in Table 10.1, “Maven Lebenszyklusphasen” (Tabelle10.1: Maven Lebenszyklusphasen) aufgeführt.

Table 10.1. Maven Lebenszyklusphasen

Lebenszyklus Phase Beschreibung

validate Validieren, dass ein Projekt komplett ist,und dass alle notwendigen Informationenum einen Build zu erstellen verfügbarsind.

generate-sources Generieren jeglicher Quellen welchebenötigt werden um in ein Kompilateingefügt zu werden.

process-sources Verarbeiten der Quellen, zum Beispiel umWerte zu Filtern

generate-resources Generieren der Ressourcen welche einemPaket beigefügt werden

process-resources Kopieren und Verarbeiten derRessourcen, Ablage in das Zielverzeichnis

Der Build Lebenszyklus

241

Page 261: Maven Definitive Guide De

Lebenszyklus Phase Beschreibung

bereit um zu packetieren.

compile Kompilieren der Quellen des Projektes.

process-classes Nachverarbeitung der generierten Dateiender Kompilierung, zum Beispiel umBytecode Modifikationen einzufügen.

generate-test-sources Generieren von Test Quellcode welcher indas Kompilat eingefügt werden muss.

process-test-sources Verarbeiten des Test Quellcodes, z.B. umWerte zu filtern

generate-test-resources Erstellen der Test Ressourcen

process-test-resources Verarbeiten und Kopieren der Ressourcenin das Test-Zielverzeichnis

test-compile Kompilieren der Test Quelldateien in dasTest Zielverzeichnis

test Abarbeiten der Tests unter Einsatz einesgeeigneten Unit Test Frameworks. DieseTests sollten nicht voraussetzen, dass derCode Packetiert und Deployed wird.

prepare-package Ausführen jeglicher notwendiger Schritte,um die Packetierung vorzubereiten, diesführt oftmals zu einer unpacketiertenverarbeiteten Version des Pakets (Teil vonMaven 2.1+)

package Paketieren des Kompilates in einverteilbares Format wie z.B. JAR, WARoder EAR

pre-integration-test Ausführen vorbereitender Schritte fürIntegrationstests. Dies kann die Erstellung

Der Build Lebenszyklus

242

Page 262: Maven Definitive Guide De

Lebenszyklus Phase Beschreibung

des notwendigen Testumfeldes umfassen.

integration-test Verarbeiten und deployen des Packages ineine Umgebung in welcherIntegrationstests ausgeführt werdenkönnen.

post-integration-test Ausführen von abschliessenden Schrittennachdem die Integrationstests ausgeführtwurden, dies kann auch die Bereinigungder Umgebung beinhalten.

verify Jegliche Prüfungen ausführen umsicherzustellen dass das Package dieerforderlichen Kriterien erfüllt.

install Installieren des Packages in das lokaleRepository um dort lokal alsAbhängigkeit für andere Projektebereitzustehen.

deploy Kopiert das endgültige Paket in das ferneRepository um dieses anderenEntwicklern und Projektenbereitzustellen. Dieser Schritt istgewöhnlich nur bei offiziellen Versionenvon Bedeutung.

10.1.3. Lebenszyklus: siteMaven kann mehr als nur Software Artefakte von Projekten erstellen. Es kann auchProjektdokumentation und Reports bezüglich dem Projekt oder eine Reihe vonProjekten, erstellen. Projektdokumentation und Site Generierung haben eineneigenen Lebenszyklus zugeordnet, welcher vier Phasen kennt:

Der Build Lebenszyklus

243

Page 263: Maven Definitive Guide De

1. pre-site

2. site

3. post-site

4. site-deployDie an den Lebenszyklus site gebundenen Goals sind:

1. site - site:site

2. site-deploy -site:deployDie Art der Packetierung ändert im allgemeinen diesen Lebenszyklus nicht, daPacketierungstypen sich primär mit der Artefakt Erzeugung befassen, nicht mit derArt des Sites der Generiert wird. Das Site Plugin started die Ausführung der DoxiaDokument Generierung und anderer Report generierender Plugins. Sie könneneinen Site eines Maven Projekts erstellen indem Sie das folgende Kommandoabsetzen:

$ mvn site

Die Generierung von Sites mittels Maven wird in Chapter 15, Site Generation(Kapitel 15: Site Generierung) weiter vertieft.

10.2. Package-spezifische LebenszyklenDie genauen Goals welche an eine Phase gebunden werden sind bestimmt durchdie Menge der Goals welche durch die projektspezifische Packetierung festgelegtwird. Ein Projekt welches vom Packetierungs Typ jar ist hat einen anderenStandardsatz Goals als das ein Projekt vom Typ war hat. Das Element packagingbeeinflusst den Satz der Goals welche beim Build abgearbeitet werden müssen.Um Ihnen beispielhaft vorzuführen wie sehr packaging den Build beeinflusst, hierzwei Projekte: Eines mit packaging-Element pom und eines vom Typ jar. DasProjekt vom Typ pom wird das Goal site:attach-descriptor in der Phase

Der Build Lebenszyklus

244

Page 264: Maven Definitive Guide De

packaging abarbeiten, das Projekt vom Typ jar wird satt dessen das Goal jar:jarabarbeiten.

The following sections describe the lifecycle for all built-in packaging types inMaven. Use these sections to find out what default goals are mapped to defaultlifecycle phases.

Der folgende Abschnitt beschreibt den Lebenszyklus aller standardmässigverfügbaren Package Typen in Maven. Benutzen Sie diesen Abschnitt als Referenzum herauszufinden welche Goals standardmässig an welche Phase gebundenwerden.

10.2.1. jarJAR ist der Standard Paketierungstyp, es ist der am häufigsten benutzte und die ammeisten in Lebenszyklen angetroffene Konfiguration. Die Standard Goals des JARLebenszyklus sind in Table 10.2, “Standard Goals des JAR Packaging” (Tabelle10.2: Standard Goals des JAR Packaging) aufgeführt.

Table 10.2. Standard Goals des JAR Packaging

Lebesnzyklus Phase Goal

process-resources resources:resources

compile compiler:compile

process-test-resources resources:testResources

test-compile compiler:testCompile

test surefire:test

package jar:jar

install install:install

deploy deploy:deploy

Der Build Lebenszyklus

245

Page 265: Maven Definitive Guide De

10.2.2. pomPOM ist der einfachste Paketierungstyp. Der generierte Artefakt ist sich selbst,kein JAR, WAR oder EAR. Es gibt keinen Code zu Testen oder Kompilieren, undes gibt auch keine Ressourcen zu verarbeiten. Die Standard Goals des POMLebenszyklus sind in Table 10.3, “Standard Goals des POM Packaging” (Tabelle10.3: Standard Goals des POM Packaging) aufgeführt:

Table 10.3. Standard Goals des POM Packaging

Lebenszyklus Phase Goal

package site:attach-descriptor

install install:install

deploy deploy:deploy

10.2.3. pluginDieser Paketierungstyp ist ähnlich dem der JAR Packetierung mit dreiUnterschieden: plugin:descriptor, plugin:addPluginArtifactMetadata, undplugin:updateRegistry. Diese Goals generieren eine Descriptor Datei und führengewisse Änderungen an den Repository Daten aus. Die Standard Goals des pluginLebenszyklus sind in Table 10.4, “Standard Goals des plugin Packaging” (Tabelle10.4: Standard Goals des plugin Packaging) aufgeführt:

Table 10.4. Standard Goals des plugin Packaging

Lebenszyklus Phase Goal

generate-resources plugin:descriptor

process-resources resources:resources

compile compiler:compile

process-test-resources resources:testResources

Der Build Lebenszyklus

246

Page 266: Maven Definitive Guide De

Lebenszyklus Phase Goal

test-compile compiler:testCompile

test surefire:test

package jar:jar, plugin:addPluginArtifactMetadata

install install:install, plugin:updateRegistry

deploy deploy:deploy

10.2.4. ejbEnterprise Java Beans (EJBs) sind eine häufing angewandte Technologie für dieModell getriebene Entwicklung von Enterprise Java Applikationen. Mavenunterstützt EJB 2 und EJB 3, obschon man das Plugin speziell konfigurieren mussum EJB 3 zu unterstützen. Standardmässig wird EJB 2.1 umgesetzt, wobei dasPlugin nach bestimmten EJB Konfigurationsdateien sucht. Die Standard Goals desejb Lebenszyklus sind in Table 10.5, “Standard Goals des ejb Packaging” (Tabelle10.5: Standard Goals des ejb Packaging) aufgeführt:

Table 10.5. Standard Goals des ejb Packaging

Lebenszyklus Phase Goal

process-resources resources:resources

compile compiler:compile

process-test-resources resources:testResources

test-compile compiler:testCompile

test surefire:test

package ejb:ejb

install install:install

deploy deploy:deploy

Der Build Lebenszyklus

247

Page 267: Maven Definitive Guide De

10.2.5. warDer Paketeierungstyp war ist ähnlich dem des Types jar oder ejb. Die Ausnahmehierbei ist das Goal war:war. Bitte beachten Sie, dass das war Plugin eine web.xml

Konfigurationsdatei im Verzeichnis src/main/webapp/WEB-INF voraussetzt. DieStandard Goals des war Lebenszyklus sind in Table 10.6, “Standard Goals des warPackaging” (Tabelle 10.6: Standard Goals des war Packaging) aufgeführt:

Table 10.6. Standard Goals des war Packaging

Lebenszyklus Phase Goal

process-resources resources:resources

compile compiler:compile

process-test-resources resources:testResources

test-compile compiler:testCompile

test surefire:test

package war:war

install install:install

deploy deploy:deploy

10.2.6. earEARs sind wohl das einfachste Java EE Konstrukt, bestehen Sie ja lediglich ausder Deployment Beschreibungsdatei (Descriptor) application.xml, Ressourcenund einigen Modulen. Das ear Plugin hat besitzt ein Goal mit dem Namengenerate-application.xml welche die applicaiton.xml Datei auf der Basis desPOM generiert. Die Standard Goals des ear Lebenszyklus sind in Table 10.7,“Standard Goals des POM Packaging” (Tabelle 10.7: Standard Goals des earPackaging) aufgeführt:

Der Build Lebenszyklus

248

Page 268: Maven Definitive Guide De

Table 10.7. Standard Goals des POM Packaging

Lebenszyklus Phase Goal

generate-resources ear:generate-application-xml

process-resources resources:resources

package ear:ear

install install:install

deploy deploy:deploy

10.2.7. Andere Packetierungs TypenDies ist keine abschliessende Liste aller für Maven verfügbarer Paketierungstypen.Es bestehen eine ganze Anzahl Paketierungs Formate verfügbar mittels externerProjekte und Plugins: der Typ native archive (NAR), die Typen SWF und SWC fürProjekte welche Adobe Flash und Flex Inhalte erzeugen und viele andere. Siekönnen ebenfalls eigene Benutzer spezifische Paketierungs Typen definieren undden standardmässigen Lebenszyklus Ihren spezifischen Bedürfnissen anpassen.

Um einen derart angepassten Paketierungstypen einzusetzen braucht es zweiDinge: ein Plugin welches den angepassten Lebenszyklus definiert und einRepository welches dieses Plugin beinhaltet. Manche angepassten PaketierungsTypen sind in Plugins definiert und über das zentrale Repository verfügbar. Hierein Beispiel eines Projekts welches sich auf das Israfil Flex Plugin stützt und einenbenutzerdefinierten Paketierungstyp SWF benutzt um die Ausgabe aus Adobe FlexQuellen zu erstellen.

Example 10.3. Benutzerdefinierter Packetierungs -Type für Adobe Flex(SWF)

<project>...<packaging>swf</packaging>...

Der Build Lebenszyklus

249

Page 269: Maven Definitive Guide De

<build><plugins><plugin>

<groupId>net.israfil.mojo</groupId><artifactId>maven-flex2-plugin</artifactId><version>1.4-SNAPSHOT</version><extensions>true</extensions><configuration>

<debug>true</debug><flexHome>${flex.home}</flexHome><useNetwork>true</useNetwork><main>org/sonatype/mavenbook/Main.mxml</main>

</configuration></plugin>

</plugins></build>...

</project>

In Section 17.6, “Plugins and the Maven Lifecycle” (Abschnitt 17.6 Plugins undder Maven Lebenszyklus) führen wir aus, wie Sie Ihren eigenen Paketierungstypenmit angepasstem Lebenszyklus erstellen. Das Beispiel sollte Ihnen aufzeigen wasnotwendig ist, um einen angepassten Paketierungstypen einzusetzen. Das einzigewas Sie tatsächlich dazu beitragen müssen ist, das Plugin welches den angepasstenPaketierungstyp mitbringt zu referenzieren. Das Israfil FLex Plugin ist einDrittpartei Maven Plugin welches von Google Code bereitgehalten wird. Fürweitere Informationen bezüglich des Plugins, oder den Schritten die notwendigsind um Adobe Flex zu Kompilieren, wenden Sie sich bitte anhttp://code.google.com/code.google.com/p/israfil-mojo. Das Plugin liefert diefolgenden Lebenszyklen des SWF Packetierungs Typ.

Table 10.8. Standard Lebenszyklus für SWF Packetierung

Lebenszyklus Phase Goal

compile flex2:compile-swc

install install

deploy deploy

Der Build Lebenszyklus

250

Page 270: Maven Definitive Guide De

10.3. Gebräuchliche Lebenszyklus GoalsViele der Paketierungs-Lebenszyklen haben ähnliche Goals. Wenn Sie die Goalsdes war und des jar Lebenszyklus vergleichen, so werden Sie feststellen, dassdiese sich lediglich in der Phase package unterscheiden. Die Phase package desLebenszyklus war ruft war:war auf, die des Lebenszyklus jar ruft jar:jar auf.Die meisten der Lebenszyklen mit welchen Sie in Kontakt kommen werden teilensich einige gemeinsame Lebenszyklus Goals um Ressourcen zu Managen, TestsAbzuarbeiten sowie Quellen zu Kompilieren. In diesem Abschnitt wollen wir nuneinige dieser gemeinsamen Lebenszyklus Goals genauer betrachten.

10.3.1. Ressourcen VerarbeitenDie meisten Lebenszyklen binden das Goal resources:resources an die Phaseprocess-resources. Diese Phase „verarbeitet“ Ressourcen und kopiert diese indas Zielverzeichnis. So Sie dieses nicht angepasst haben, gelten die im Super POMfestegelegten Standardverzeichnisse, das heisst, Maven kopiert die Dateien von${basedir}/src/main/resources nach ${project.build.outputDirectory}.Zusätzlich zu der reinen Kopieraktion kann Maven auch die Ressourcen filtern,d.h. Sie können gewisse Token in den Dateien ersetzen. Genau wie Variableninnerhalb der POM Datei mittels ${ … } markiert sind, so können Sie die gleicheSyntax einsetzen um Variablen innerhalb von Ressourcen zu referenzieren. ImZusammenspiel mit Build-Profilen kann diese Eigenschaft dazu benutzt werden,um Builds für unterschiedliche Zielplattformen zu erstellen. Dies trifft man häufigin Umgebungen an, welche für das gleiche Projekt unterschiedliche Builds für dasDevelopment, Test, Integrationstest und Produktion erstellen müssen. Bezüglichweiter Informationen zu Build-Profilen verweisen wir auf Chapter 11, BuildProfiles (Kapitel 11: Build Profile).

Um Ressource Filterung zu veranschaulichen, stellen Sie sich vor, Sie habe einProjekt mit einer XML-Datei unter/src/main/resources/META-INF/services.xml. Hierbei möchten Sie eineKonfigurationseinstellung in eine Property-Datei auslagern. In anderen Worten,Sie möchten zum Beispiel die Werte der JDBC URL, Benutzername und Passwort

Der Build Lebenszyklus

251

Page 271: Maven Definitive Guide De

der Datenbank nicht direkt in der service.xml Datei ablegen. Statt dessenmöchten Sie eine Property Datei mit den variablen Werten Ihres Programmsanlegen. Das ermöglicht Ihnen, alle Konfigurationsparameter in einer Dateizusammenzufassen, um diese an einem Ort zu ändern sobald Sie von einerUmgebung in eine andere wechseln. Sehen Sie sich also zuallererts den Inhalt derentsprechenden Datei services.xml unter /src/main/resources/META-INF an:

Example 10.4. Einsatz von Properties in den Projektressourcen

<service><!-- This URL was set by project version ${project.version} --><url>${jdbc.url}</url><user>${jdbc.username}</user><password>${jdbc.password}</password>

</service>

Die in der XML Datei zum Einsatz kommende Syntax ist die selbe welche bereitsin der POM Datei zum Einsatz kommt. Die erste zum Einsatz kommende Variableist sogar eine Maven-implizite Variable welche vom POM bereitgestellt wird. DieVariable projekt erlaubt den Zugriff auf die Werte des POM. Die folgenden dreiReferenzen sind jdbc.url, jdbc.username und jdbc.passwort. Diesebenutzerdefinierten Variablen wurden in der Property Datei untersrc/main/filters/default.properties definiert.

Example 10.5. Datei default.properties unter src/main/filters

jdbc.url=jdbc:hsqldb:mem:mydbjdbc.username=sajdbc.password=

Um Ressourcenfilterung der Datei default.properties einzustellen, müsseninnerhalb des POM zwei Dinge gesetzt werden: eine Liste der properties-Dateieninnerhalb des Elements filters der Buildkonfiguration, sowie eine MavenEinstellung dass das Ressourcenverzeichnis der Filterung unterliegt.Standardmässig ist dieses Verhalten abgestellt und Ressourcen werden direkt indas Ausgabeverzeichnis kopiert, oder Maven übergeht diesen Schritt ganz. Diese

Der Build Lebenszyklus

252

Page 272: Maven Definitive Guide De

Einstellung ist standardmässig so gewählt, damit es nicht vorkommt, dass ohneersichtlichen Grund alle bestehenden ${...}-Referenzen ersetzt werden, welcheSie gar nicht ersetzen wollten.

Example 10.6. Ressourcen Filtern (Ersetzen von Properties)

<build><filters>

<filter>src/main/filters/default.properties</filter></filters><resources>

<resource><directory>src/main/resources</directory><filtering>true</filtering>

</resource></resources>

</build>

Wie mit allen Verzeichnissen unter Maven, ist es nicht Bedingung, das dasRessourcenverzeichnis unbedingt unter src/main/ressources liegt. Das istlediglich der entsprechende Standardpfad wie er im Super POM festgelegt ist.Ebenfalls ist es gut zu wissen, dass Sie nicht gezwungen sind, alle Ihre Ressourcenin ein einzigen Verzeichniss zu konsolidieren. Sie können Ressourcen auch ingetrennten Verzeichnissen unter src/main ablegen. Nehmen Sie einmal an, Siearbeiten an einem Projekt mit hunderten von XML-Dokumenten und hundertenvon Bildern. Anstatt nun alle diese Dateien unter dem Verzeichnissrc/main/ressources gemischt abzulegen, ist es möglich, zwei Verzeichnissesrc/main/xml und src/main/images anzulegen um die Inhalte dort abzulegen.Um weitere Verzeichnisse Ihrer Liste von Ressourceverzeichnissen zuzufügen,müssen Sie folgendes Element resource Ihrer Buildkonfiguration zufügen:

Example 10.7. Festlegen zusätzlicher Ressource Verzeichnisse

<build>...<resources>

<resource><directory>src/main/resources</directory>

</resource><resource>

Der Build Lebenszyklus

253

Page 273: Maven Definitive Guide De

<directory>src/main/xml</directory></resource><resource><directory>src/main/images</directory>

</resource></resources>...

</build>

Sollten Sie ein Projekt erstellen, welches eine Befehlszeilenapplikation erzeugt, sofinden Sie sich oftmals in einer Situation wieder, in welcher Sie einfache ShellSkripte erstellen, welche ein Jar-Dateien des Builds referenzieren. Sollten Sie danndas Assembly Plugin benutzen um ein Distributionspaket in Form eines ZIP- oderTAR-Archivs zu erstellen, so macht es Sinn, alle ihre Skripte in einemVerzeichniss wie etwa src/main/command zusammenzufassen. Im folgendenAusschnitt einer POM Ressourcenkonfiguration können Sie sehen, wie wirRessourcenfilterung und eine Referenz auf eine Projektvariable benutzen können,um den endgültigen Ausgabenamen des jar-Archives zu definieren. WeitereInformationen bezüglich des Maven Assembly Plugins finden Sie in Chapter 12,Maven Assemblies (Kapitel 12: Maven Assemblies).

Example 10.8. Filterung von Skript Ressourcen

<build><groupId>org.sonatype.mavenbook</groupId><artifactId>simple-cmd</artifactId><version>2.3.1</version>...<resources>

<resource><filtering>true</filtering><directory>${basedir}/src/main/command</directory><includes>

<include>run.bat</include><include>run.sh</include>

</includes><targetPath>${basedir}</targetPath>

</resource><resource><directory>${basedir}/src/main/resources</directory>

</resource></resources>...

</build>

Der Build Lebenszyklus

254

Page 274: Maven Definitive Guide De

Der Aufruf von mvn process-resources dieses Projektes erzeugt zwei Dateien,run.sh sowie run.bat, beide unter ${basedir}. Wir haben innerhalb einesElements resource diese zwei Dateien herausgepickt, Filterung eingestellt, undden Zielpfad (targetPath) auf ${basedir} gesetzt. In einem zweiten ElememtResource haben wir definiert, dass die Ressourcen des Standard Ressourcenpfadesin das Standardausgabeverzeichnis kopiert werden ohne diese zu filtern.Example 10.8, “Filterung von Skript Ressourcen” (Beispiel 10.8: Filterung vonSkript Ressourcen) zeigt beispielhaft auf, wie Sie zwei Resourceverzeichnissebestimmen und diese mit unterschiedlichen Filterkriterien sowieZielverzeichnissen versehen. Das Projekt des Example 10.8, “Filterung von SkriptRessourcen” (Beispiels 10.8: Filterung von Skript Ressourcen) würde eine Dateirun.bat unter src/main/command mit folgendem Inhalt enthalten:

@echo offjava -jar ${project.build.finalName}.jar %*

Nach der Ausführung von mvn process-resources, erscheint eine Datei run.batim Verzeichnis ${basedir} mit folgendem Inahlt:

@echo offjava -jar simple-cmd-2.3.1.jar %*

Die Möglichkeit unterschiedliche Filter für speziefische Untergruppen vonRessourcen zu setzen, ist ein weiterere Grund weshalb Projekte mit vielenverschiedenen Arten von Artefakten es oftmals als hilfreich erachten, diese inverschiedenen Verziechnissen abzulegen. Die Alternative zum Ablegenverschiedener Ressourcetypen in unterschiedlichen Verzichnissen ist der Einsatzvon komplexeren Sätzen von Ein- und Ausschlusskriterien, um die entsprechendenRessource Dateien welche einem Muster genügen, zu spezifizieren.

10.3.2. compileDie allermeisten Lebenszyklen binden das Goal compile des Compiler Plugin andie Phase compile. Diese Phase ruft compile:compile auf, welches konfiguriertist, alle Quellen zu verarbeiten und den resultierenden Bytecode im BuildAusgabeverzeichniss abzulegen. Sollten Sie die Standardeinstellungen des

Der Build Lebenszyklus

255

Page 275: Maven Definitive Guide De

SuperPOM nicht angepasst haben, so wird compile:compile alle Quellen untersrc/main/java verarbeiten und das Ergebnis unter /target/classes ablegen. DasCompile Plugin ruft javac™ auf, und hat die Standardeinstellungen 1.3 und 1.1, inanderen Worten, das Compiler Plugin geht davon aus, dass Sie Java 1.3 konformenCode erzeugen und diesen an eine Java Virtual Machine 1.1 addressieren. SolltenSie diese Einstellungen verändern wollen, so müssen sie die Ziel undQuellkonfiguration ihres Projektes dem Compiler Plugin mit auf den Weg geben.Sie tun dies mittels einem Eintrag in der POM Datei wie dies in Example 10.9,“Einstellen der Quell- und Zielkonfiguration des Compiler Plugins” (Beispiel 10.9:Einstellen der Quell- und Zielkonfiguration des Kompiler Plugins).

Example 10.9. Einstellen der Quell- und Zielkonfiguration des CompilerPlugins

<project>...<build>

...<plugins><plugin>

<artifactId>maven-compiler-plugin</artifactId><configuration>

<source>1.5</source><target>1.5</target>

</configuration></plugin>

</plugins>...

</build>...

</project>

Beachten Sie, dass wir das Compiler Plugin konfigurieren, und nicht dasspezifische Goal compile:compile. Um nur das spezifische Goalcompile:compile zu konfigurieren, müssten wir das Element configurationunterhalb eines Elements execution des Goals compile:compile spezifizieren.Wir haben aber das Quell-und Zielverzeichnis des Compile Plugins konfiguriert,denn wir wollten nicht nur dieses eine Goal konfigurieren. Das Compiler Pluginkommt ebenfalls zum Einsatz, wenn Maven die Testquellen mittels dem Goal

Der Build Lebenszyklus

256

Page 276: Maven Definitive Guide De

compile:testCompile verarbeitet. Die Definition des Plugin erlaubt uns dieKonfiguration einmal auf der Pluginebene für alle Goals vorzunehmen.

Sollten Sie den Ort Ihrer Quelldateien umdefinieren müssen, so können Sie diesdurch die Veränderung der Build Konfiguration erreichen. Sollten Sie zumBeispiel ihre Quellen unter src/java statt src/main/java ablegen, und sollten dasErgebnis unter classes anstatt target/classes ablegen wollen, so können Siejederzeit die Standarwerte sourceDirectory (Quellverzeichnis) des SuperPOMübersteuern.

Example 10.10. Übersteuern des Standard Quellverzeichnisses

<build>...<sourceDirectory>src/java</sourceDirectory><outputDirectory>classes</outputDirectory>...

</build>

WarningObschon es Gründe geben mag die dafür sprechen, Maven ihrenWünschen bezüglich Verzeichnisstruktur anzupassen, so können wir hiernicht stark genug betonen, dass es besser ist Ihre eingene Strukturzugunsten der Maven Standardverzeichnisstruktur aufzugeben. Dies istkeinesfalls darin begründet, dass wir Sie von dieser Struktur bekehrenwollen, sondern es ist einfacher für andere Entwickler, ihreProjektstruktur zu verstehen, sollte diese den grundlegendenKonventionen folgen. Vergessen Sie schon den Ansatz! - Tun Sie sichdiesen Gefallen.

10.3.3. Verarbeiten von Test RessourcenDie Phase process-test-resources lässt sich kaum von der Phaseprocess-ressources unterscheiden. Es gibt einige triviale Unterschiede des POM,alles andere ist gleich. Sie können Test Ressourcen filtern wie die anderen

Der Build Lebenszyklus

257

Page 277: Maven Definitive Guide De

Ressourcen auch. Der standardmässige Ort der Testressourcen ist im Super POMmit src/test/resources gesetzt und das standardmässige Ausgabeverzeichnisvon Testressourcen auf target/test-classes wie dies unter${project.build.testOutputDirectory} definiert ist.

10.3.4. Kompilieren der Test Klassen (testCompile)Die Phase test-compile ist beinahe identisch mit der Phase compile. Der einzigeUnterschied ist, dass test-compile das Goal compile:testCompile anzieht, umTestquellen vom Quellverzeichnis anzuziehen und die Ausgabe imTest-Ausgabeverzeichnis abzulegen. Sollten Sie die Standardverzeichnisse desSuperPOM nicht übersteuert haben, so wird compile:Test die Sourcen aus demVerzeichnis src/test/java in das Verzeichnis target/test-classeskompilieren.

Wie mit den Quellverzeichnissen gilt, sollten Sie den Ort der Test Verzeichnisseändern wollen, so können Sie dies tun indem Sie testSourceDirectory undtestOutputDirectory anpassen. Sollten Sie Testquellen unter src-test/ anstattsrc/test/java ablegen und den Bytecode unter classes-test/ anstatttarget/test-classes ablegen wollen, so wüden Sie auf die folgendeKonfiguration vornehmen.

Example 10.11. Übersteuern des Ortes der Testquellen und -ausgabe

<build>...<testSourceDirectory>src-test</testSourceDirectory><testOutputDirectory>classes-test</testOutputDirectory>...

</build>

10.3.5. testDie meisten Lebenszyklen binden das Goal test des Surefire Plugin an die Phasetest. Das Surefire Plugin ist das UnitTest Plugin von Maven. DasStandardverhalten des Surefire Plugin ist, nach allen Klassen welche im Test

Der Build Lebenszyklus

258

Page 278: Maven Definitive Guide De

Quellenverzeichnis auf *Test enden zu suchen, um diese als JUnit Testsauszuführen. Das Surefire Plugin kann auch konfiguriert werden die Tests alsTestNG Unit Test abzuarbeiten.

Nach der Ausführung von mvn test sollten Sie feststellen können, das das SurefirePlugin eine Anzahl Reports unter target/surefire-reports ablegt. DiesesReportverzeichnis beinhaltet zwei Dateien für jeden ausgeführten Test: eine XMLDatei welche Informationen bezüglich des Tests enthält und eine Textdatei welchedie Ausgabe des UnitTest enthält. Sollte ein Problem während der Phase test

auftreten und ein Unit Test nicht bestehen, so können Sie die Ausgabe von Mavenzusammen mit dieser Datei benutzen, um den Grund des Abbruchs zu finden.Dieses Verzeichnis surefire-reports/ kommt ebenfalls zum Einsatz währendder Site Generierung wobei eine einfach zu lesende Zusammenfassung der UnitTests des Projektes erstellt wird.

Sollten Sie an einem Projekt arbeiten, bei welchem einige abbrechende Unit Testbestehen, Sie möchten aber dennoch eine Ausgabe erzeugen, so müssen Sie dasSurefire Plugin so konfigurieren, dass dieses die Ausführung beim Auftreten einesFehlers nicht abbricht. Das Standardverhalten ist, dass der Build beim erstenauftreten eines Unit Test Fehlers abbricht. Um dieses Verhalten zu ändern, müssenSie das Property testFailureIgnore des SureFire Plugin auf true setzen.

Example 10.12. Konfiguration des Surefire Plugins um Testabbrüche zuignorieren

<build><plugins>

<plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-surefire-plugin</artifactId><configuration>

<testFailureIgnore>true</testFailureIgnore></configuration></plugin>...

</plugins></build>

Sollten Sie die Tests komplett übergehen wollen, so können Sie dies durch den

Der Build Lebenszyklus

259

Page 279: Maven Definitive Guide De

folgenden Aufruf erreichen:

$ mvn install -Dmaven.test.skip=true

Die Variable maven.test.skip beeinflusst beides, den Compiler und das SurefirePlugin. Geben Sie maven.test.skip=true an, so weisen Sie Maven an die Testsinsgesamt zu ignorieren.

10.3.6. installDas Goal install des Install Plugin ist immer an die Lebenszyklusphase install

gebunden. Das Goal install:install installiert den Artefakt in das lokaleRepository. Sollten Sie ein Projekt mit der groupId von org.sonatype.mavenbook

einen Artefakten mit artifactId von simple-test und einer version von 1.0.2

haben, so wird das Goal install:install das JAR vontarget/simple-test-1.0.2.jar in~/.m2/repository/org/sonatype/mavenbook/simple-test/1.0.2/simpletest-1.0.2.jar

kopieren. Ist das Projekt ein Projekt vom Typ POM Pakageing, so wird das POMin das lokale Repository eingestellt.

10.3.7. deployDas Goal deploy des Plugins Deploy wird gewöhnlich an die Lebenszyklusphasedeploy gebunden. Diese Phase wird benutzt, um einen Artefakten in ein entferntesRepository einzustellen. Gewöhnlich geschiet dies um ein entferntes Repositorybei einer Freigabe aktuell zu halten. Der Deployment Vorgang kann eine einfacheKopieraktion einer Datei darstellen, oder aber auch eine Übertragung einer Dateimittles SCP und einem publicKey. Deployment Einstellungen beinhaltengewöhnlich Zugangsdaten zu einem entfernten Repository, aus welchem GrundDeployment Einstellungen gewöhnlich nicht in der pom.xml Datei gehaltenwerden. Statt dessen gehen diese gewöhnlich in die benutzerspezifische Datei~/m2/settings.xml. Zu diesem Zeitpunkt reicht es zu wissen, das das Goaldeploy:deploy an die Phase deploy gebunden ist und sich der Übertragung derArtefakten zu einem veröffentlichten Repository und dem damit verbundenenUpdate der Repository Informationen welche von einem solchen Vorgang

Der Build Lebenszyklus

260

Page 280: Maven Definitive Guide De

betroffen sein könnten, annimmt.

Der Build Lebenszyklus

261

Page 281: Maven Definitive Guide De

Chapter 11. Build Profiles

11.1. What Are They For?Profiles allow for the ability to customize a particular build for a particularenvironment; profiles enable portability between different build environments.

What do we mean by different build environments? Two example buildenvironments are production and development. When you are working in adevelopment environment, your system might be configured to read from adevelopment database instance running on your local machine while in production,your system is configured to read from the production database. Maven allows youto define any number of build environments (build profiles) which can overrideany of the settings in the pom.xml. You could configure your application to readfrom your local, development instance of a database in your "development" profile,and you can configure it to read from the production database in the "production"profile. Profiles can also be activated by the environment and platform, you cancustomize a build to run differently depending the Operating System or theinstalled JDK version. Before we talk about using and configuring Maven profiles,we need to define the concept of Build Portability.

11.1.1. What is Build PortabilityA build's "portability" is a measure of how easy it is to take a particular project andbuild it in different environments. A build which works without any customconfiguration or customization of properties files is more portable than a buildwhich requires a great deal of work to build from scratch. The most portableprojects tend to be widely used open source projects like Apache Commons ofApache Velocity which ship with Maven builds which require little or nocustomization. Put simply, the most portable project builds tend to just work, outof the box, and the least portable builds require you to jump through hoops andconfigure platform specific paths to locate build tools. Before we show you how toachieve build portability, let's survey the different kinds of portability we are

262

Page 282: Maven Definitive Guide De

talking about.

11.1.1.1. Non-Portable BuildsThe lack of portability is exactly what all build tools are made to prevent -however, any tool can be configured to be non-portable (even Maven). Anon-portable project is buildable only under a specific set of circumstances andcriteria (e.g., your local machine). Unless you are working by yourself and youhave no plans on ever deploying your application to another machine, it is best toavoid non-portability entirely. A non-portable build only runs on a single machine,it is a "one-off". Maven is designed to discourage non-portable builds by offeringthe ability to customize builds using profiles.

When a new developer gets the source for a non-portable project, they will not beable to build the project without rewriting large portions of a build script.

11.1.1.2. Environment PortabilityA build exhibits environment portability if it has a mechanism for customizingbehavior and configuration when targeting different environments. A project thatcontains a reference to a test database in a test environment, for example, and aproduction database in a production environment, is environmentally portable. It islikely that this build has a different set of properties for each environment. Whenyou move to a different environment, one that is not defined and has no profilecreated for it, the project will not work. Hence, it is only portable between definedenvironments.

When a new developer gets the source for an environmentally portable project,they will have to run the build within a defined environment or they will have tocreate a custom environment to successfully build the project.

11.1.1.3. Organizational (In-House) PortabilityThe center of this level of portability is a project's requirement that only a selectfew may access internal resources such as source control or aninternally-maintained Maven repository. A project at a large corporation maydepend on a database available only to in-house developers, or an open source

Build Profiles

263

Page 283: Maven Definitive Guide De

project might require a specific level of credentials to publish a web site anddeploy the products of a build to a public repository.

If you attempt to build an in-house project from scratch outside of the in-housenetwork (for example, outside of a corporate firewall), the build will fail. It mayfail because certain required custom plugins are unavailable, or projectdependencies cannot be found because you don't have the appropriate credentialsto retrieve dependencies from a custom remote repository. Such a project isportable only across environments in a single organization.

11.1.1.4. Wide (Universal) PortabilityAnyone may download a widely portable project's source, compile, and install itwithout customizing a build for a specific environment. This is the highest level ofportability; anything less requires extra work for those who wish to build yourproject. This level of portability is especially important for open source projects,which depend on the ability for would-be contributors to easily download andbuild from source.

Any developer could download the source for a widely portable project.

11.1.2. Selecting an Appropriate Level of PortabilityClearly, you'll want to avoid creating the worst-case scenario: the non-portablebuild. You may have had the misfortune to work or study at an organization thathad critical applications with non-portable builds. In such organizations, youcannot deploy an application without the help of a specific individual on a specificmachine. In such an organization, it is also very difficult to introduce new projectdependencies or changes without coordinating the change with the single personwho maintains such a non-portable build. Non-portable builds tend to grow inhighly political environments when one individual or group needs to exert controlover how and when a project is built and deployed. "How do we build the system?Oh, we've got to call Jack and ask him to build it for us, no one else deploys toproduction." That is a dangerous situation which is more common that you wouldthink. If you work for this organization, Maven and Maven profiles provide a wayout of this mess.

Build Profiles

264

Page 284: Maven Definitive Guide De

On the opposite end of the portability spectrum are widely portable builds. Widelyportable builds are generally the most difficult build systems to attain. These buildsrestrict your dependencies to those projects and tools that may be freely distributedand are publicly available. Many commercial software packages might be excludedfrom the most-portable builds because they cannot be downloaded before you haveaccepted a certain license. Wide portability also restricts dependencies to thosepieces of software that may be distributed as Maven artifacts. For example, if youdepend upon Oracle JDBC drivers, your users will have to download and installthem manually; this is not widely portable as you will have to distribute a set ofenvironment setup instructions for people interested in building your application.On the other hand, you could use a JDBC driver which is available from the publicMaven repositories like MySQL or HSQLDB.

As stated previously, open source projects benefit from having the most widelyportable build possible. Widely portable builds reduce the inefficiencies associatedwith contributing to a project. In an open source project (such as Maven) there aretwo distinct groups: end-users and developers. When an end-user uses a projectlike Maven and decides to contribute a patch to Maven, they have to make thetransition from using the output of a build to running a build. They have to firstbecome a developer, and if it is difficult to learn how to build a project, thisend-user has a disincentive to take the time to contribute to a project. In a widelyportable project, an end-user doesn't have to follow a set or arcane buildinstructions to start becoming a developer, they can download the source, modifythe source, build, and submit a contribution without asking someone to help themset up a build environment. When the cost of contributing source back to anopen-source project is lower, you'll see an increase in source code contributions,especially casual contributions which can make the difference between a project'ssuccess and a project's failure. One side-effect of Maven's adoption across a widegroup of open source projects is that it has made it easier for developers tocontribute code to various open source projects.

11.2. Portability through Maven ProfilesA profile in Maven is an alternative set of configuration values which set or

Build Profiles

265

Page 285: Maven Definitive Guide De

override default values. Using a profile, you can customize a build for differentenvironments. Profiles are configured in the pom.xml and are given an identifier.Then you can run Maven with a command-line flag that tells Maven to executegoals in a specific profile. The following pom.xml uses a production profile tooverride the default settings of the Compiler plugin.

Example 11.1. Using a Maven Profile to Override Production CompilerSettings

<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/maven-v4_0_0.xsd"><modelVersion>4.0.0</modelVersion><groupId>org.sonatype.mavenbook</groupId><artifactId>simple</artifactId><packaging>jar</packaging><version>1.0-SNAPSHOT</version><name>simple</name><url>http://maven.apache.org</url><dependencies>

<dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>3.8.1</version><scope>test</scope>

</dependency></dependencies><profiles>#

<profile><id>production</id>#<build>#

<plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><configuration>

<debug>false</debug>#<optimize>true</optimize>

</configuration></plugin>

</plugins></build>

</profile></profiles>

</project>

Build Profiles

266

Page 286: Maven Definitive Guide De

In this example, we've added a profile named production that overrides the defaultconfiguration of the Maven Compiler plugin, let's examine the syntax of thisprofile in detail.

❶ The profiles element is in the pom.xml, it contains one or more profile

elements. Since profiles override the default settings in a pom.xml, theprofiles element is usually listed as the last element in a pom.xml.

❷ Each profile has to have an id element. This id element contains the namewhich is used to invoke this profile from the command-line. A profile isinvoked by passing the -P<profile_id> command-line argument to Maven.

❸ A profile element can contain many of the elements which can appear underthe project element of a POM XML Document. In this example, we'reoverriding the behavior of the Compiler plugin and we have to override theplugin configuration which is normally enclosed in a build and a plugins

element.❹ We're overriding the configuration of the Maven Compiler plugin. We're

making sure that the bytecode produced by the production profile doesn'tcontain debug information and that the bytecode has gone through thecompiler's optimization routines.

To execute mvn install under the production profile, you need to pass the-Pproduction argument on the command-line. To verify that the production

profile overrides the default Compiler plugin configuration, execute Maven withdebug output enabled (-X) as follows:

~/examples/profile $ mvn clean install -Pproduction -X... (omitting debugging output) ...[DEBUG] Configuring mojo 'o.a.m.plugins:maven-compiler-plugin:2.0.2:testCompile'[DEBUG] (f) basedir = ~\examples\profile[DEBUG] (f) buildDirectory = ~\examples\profile\target...[DEBUG] (f) compilerId = javac[DEBUG] (f) debug = false[DEBUG] (f) failOnError = true[DEBUG] (f) fork = false[DEBUG] (f) optimize = true[DEBUG] (f) outputDirectory = \

~\svnw\sonatype\examples\profile\target\test-classes[DEBUG] (f) outputFileName = simple-1.0-SNAPSHOT[DEBUG] (f) showDeprecation = false[DEBUG] (f) showWarnings = false[DEBUG] (f) staleMillis = 0

Build Profiles

267

Page 287: Maven Definitive Guide De

[DEBUG] (f) verbose = false[DEBUG] -- end configuration --... (omitting debugging output) ...

This excerpt from the debug output of Maven shows the configuration of theCompiler plugin under the production profile. As shown in the output, debug is setto false and optimize is set to true.

11.2.1. Overriding a Project Object ModelWhile the previous example showed you how to override the default configurationproperties of a single Maven plugin, you still don't know exactly what a Mavenprofile is allowed to override. The short-answer to that question is that a Mavenprofile can override almost everything that you would have in a pom.xml. TheMaven POM contains an element under project called profiles containing aproject's alternate configurations, and under this element are profile elementswhich define each profile. Each profile must have an id, and other than that, it cancontain almost any of the elements one would expect to see under project. Thefollowing XML document shows all of the elements, a profile is allowed tooverride.

Example 11.2. Elements Allowed in a Profile

<project><profiles>

<profile><build>

<defaultGoal>...</defaultGoal><finalName>...</finalName><resources>...</resources><testResources>...</testResources><plugins>...</plugins>

</build><reporting>...</reporting><modules>...</modules><dependencies>...</dependencies><dependencyManagement>...</dependencyManagement><distributionManagement>...</distributionManagement><repositories>...</repositories><pluginRepositories>...</pluginRepositories><properties>...</properties>

</profile>

Build Profiles

268

Page 288: Maven Definitive Guide De

</profiles></project>

A profile can override an element shown with ellipses. A profile can override thefinal name of a project's artifact in a profile, the dependencies, and the behavior ofa project's build via plugin configuration. A profile can also override theconfiguration of distribution settings depending on the profile; for example, if youneed to publish an artifact to a staging server in a staging profile, you would createa staging profile which overrides the distributionManagement element in aprofile.

11.3. Profile ActivationIn the previous section we showed you how to create a profile that overridesdefault behavior for a specific target environment. In the previous build the defaultbuild was designed for development and the production profile exists to provideconfiguration for a production environment. What happens when you need toprovide customizations based on variables like operating systems or JDK version?Maven provides a way to "activate" a profile for different environmentalparameters, this is called profile activation.

Take the following example, assume that we have a Java library that has a specificfeature only available in the Java 6 release: the Scripting Engine as defined inJSR-223. You've separated the portion of the library that deals with the scriptinglibrary into a separate Maven project, and you want people running Java 5 to beable to build the project without attempting to build the Java 6 specific libraryextension. You can do this by using a Maven profile that adds the script extensionmodule to the build only when the build is running within a Java 6 JDK. First, let'stake a look at our project's directory layout and how we want developers to buildthe system.

When someone runs mvn install with a Java 6 JDK, you want the build to includethe simple-script project's build, when they are running in Java 5, you would liketo skip the simple-script project build. If you failed to skip the simple-script

Build Profiles

269

Page 289: Maven Definitive Guide De

project build in Java 5, your build would fail because Java 5 does not have theScriptEngine on the classpath. Let's take a look at the library project's pom.xml:

Example 11.3. Dynamic Inclusion of Submodules Using Profile Activation

<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/maven-v4_0_0.xsd"><modelVersion>4.0.0</modelVersion><groupId>org.sonatype.mavenbook</groupId><artifactId>simple</artifactId><packaging>jar</packaging><version>1.0-SNAPSHOT</version><name>simple</name><url>http://maven.apache.org</url><dependencies>

<dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>3.8.1</version><scope>test</scope>

</dependency></dependencies><profiles>

<profile><id>jdk16</id><activation>#

<jdk>1.6</jdk></activation><modules>#

<module>simple-script</module></modules>

</profile></profiles>

</project>

If you run mvn install under Java 1.6, you will see Maven descending into thesimple-script subdirectory to build the simple-script project. If you arerunning mvn install in Java 1.5, the build will not try to build the simple-script

submodule. Exploring this activation configuration in more detail:

❶ The activation element lists the conditions for profile activation. In thisexample, we've specified that this profile will be activated by Java versionsthat begin with "1.6". This would include "1.6.0_03", "1.6.0_02", or any

Build Profiles

270

Page 290: Maven Definitive Guide De

other string that began with "1.6". Activation parameters are not limited toJava version, for a full list of activation parameters, see ActivationConfiguration.

❷ In this profile we are adding the module simple-script. Adding this modulewill cause Maven to look in the simple-script/ subdirectory for a pom.xml.

11.3.1. Activation ConfigurationActivations can contain one of more selectors including JDK versions, OperatingSystem parameters, files, and properties. A profile is activated when all activationcriteria has been satisfied. For example, a profile could list an Operating Systemfamily of Windows, and a JDK version of 1.4, this profile will only be activatedwhen the build is executed on a Windows machine running Java 1.4. If the profileis active then all elements override the corresponding project-level elements as ifthe profile were included with the -P command-line argument. The followingexample, lists a profile which is activated by a very specific combination ofoperating system parameters, properties, and a JDK version.

Example 11.4. Profile Activation Parameters: JDK Version, OS Parameters,and Properties

<project>...<profiles>

<profile><id>dev</id><activation>

<activeByDefault>false</activeByDefault>#<jdk>1.5</jdk>#<os>

<name>Windows XP</name>#<family>Windows</family><arch>x86</arch><version>5.1.2600</version>

</os><property>

<name>mavenVersion</name>#<value>2.0.5</value>

</property><file>

<exists>file2.properties</exists>#<missing>file1.properties</missing>

</file>

Build Profiles

271

Page 291: Maven Definitive Guide De

</activation>...

</profile></profiles>

</project>

This previous example defines a very narrow set of activation parameters. Let'sexamine each activation criterion in detail:

❶ The activeByDefault element controls whether this profile is consideredactive by default.

❷ This profile will only be active for JDK versions that begin with "1.5". Thisincludes "1.5.0_01", "1.5.1".

❸ This profile targets a very specific version of Windows XP, version 5.1.2600on a 32-bit platform. If your project uses the native plugin to build a Cprogram, you might find yourself writing projects for specific platforms.

❹ The property element tells Maven to activate this profile if the propertymavenVersion is set to the value 2.0.5. mavenVersion is an implicit propertythat is available to all Maven builds.

❺ The file element allows us to activate a profile based on the presence (orabsence) of files. The dev profile will be activated if a file namedfile2.properties exists in the base directory of the project. The dev profilewill only be activated if there is no file named file1.properties file in thebase directory of the project.

11.3.2. Activation by the Absence of a PropertyYou can activate a profile based on the value of a property like environment.type.You can activate a development profile if environment.type equals dev, or aproduction profile if environment.type equals prod. You can also activate aprofile in the absence of a property. The following configuration activates a profileif the property environment.type is not present during Maven execution.

Example 11.5. Activating Profiles in the Absence of a Property

Build Profiles

272

Page 292: Maven Definitive Guide De

<project>...<profiles>

<profile><id>development</id><activation>

<property><name>!environment.type</name>

</property></activation>

</profile></profiles>

</project>

Note the exclamation point prefixing the property name. The exclamation point isoften referred to as the "bang" character and signifies "not". This profile isactivated when no ${environment.type} property is set.

11.4. Listing Active ProfilesMaven profiles can be defined in either pom.xml, profiles.xml,~/.m2/settings.xml, or ${M2_HOME}/conf/settings.xml. With these four levels,there's no good way of keeping track of profiles available to a particular projectwithout remembering which profiles are defined in these four files. To make iteasier to keep track of which profiles are available, and where they have beendefined, the Maven Help plugin defines a goal, active-profiles, which lists allthe active profiles and where they have been defined. You can run theactive-profiles goal, as follows:

$ mvn help:active-profilesActive Profiles for Project 'My Project':

The following profiles are active:

- my-settings-profile (source: settings.xml)- my-external-profile (source: profiles.xml)- my-internal-profile (source: pom.xml)

Build Profiles

273

Page 293: Maven Definitive Guide De

11.5. Tips and TricksProfiles can encourage build portability. If your build needs subtle customizationsto work on different platforms or if you need your build to produce different resultsfor different target platforms, project profiles increase build portability. Settingsprofiles generally decrease build portability by adding extra-project informationthat must be communicated from developer to developer. The following sectionsprovide some guidelines and some ideas for applying Maven profiles to yourproject.

11.5.1. Common EnvironmentsOne of the core motivations for Maven project profiles was to provide forenvironment-specific configuration settings. In a development environment, youmight want to produce bytecode with debug information and you might want toconfigure your system to use a development database instance. In a productionenvironment you might want to produce a signed JAR and configure the system touse a production database. In this chapter, we defined a number of environmentswith identifiers like dev and prod. A simpler way to do this would be to defineprofiles that are activated by environment properties and to use these commonenvironment properties across all of your projects. For example, if every projecthad a development profile activated by a property named environment.type

having a value of dev, and if those same projects had a production profileactivated by a property named environment.type having a value of prod, youcould create a default profile in your settings.xml that always setenvironment.type to dev on your development machine. This way, each projectdefines a dev profile activated by the same environment variable. Let's see howthis is done, the following settings.xml defines a profile in ~/.m2/settings.xml

which sets the environment.type property to dev.

Example 11.6. ~/.m2/settings.xml defines a default profile settingenvironment.type

<settings>

Build Profiles

274

Page 294: Maven Definitive Guide De

<profiles><profile><activation>

<activeByDefault>true</activeByDefault></activation><properties>

<environment.type>dev</environment.type></properties>

</profile></profiles>

</settings>

This means that every time you run Maven on your machine, this profile will beactivated and the property environment.type will have the value dev. You canthen use this property to activate profiles defined in a project's pom.xml as follows.Let's take a look at how a project's pom.xml would define a profile activated byenvironment.type having the value dev.

Example 11.7. Project Profile Activated by environment.type equalling 'dev'

<project>...<profiles>

<profile><id>development</id><activation>

<property><name>environment.type</name><value>dev</value>

</property></activation><properties>

<database.driverClassName>com.mysql.jdbc.Driver</database.driverClassName><database.url>

jdbc:mysql://localhost:3306/app_dev</database.url><database.user>development_user</database.user><database.password>development_password</database.password>

</properties></profile><profile><id>production</id><activation>

<property><name>environment.type</name><value>prod</value>

</property>

Build Profiles

275

Page 295: Maven Definitive Guide De

</activation><properties>

<database.driverClassName>com.mysql.jdbc.Driver</database.driverClassName><database.url>jdbc:mysql://master01:3306,slave01:3306/app_prod</database.url><database.user>prod_user</database.user>

</properties></profile>

</profiles></project>

This project defines some properties like database.url and database.user whichmight be used to configure another Maven plugin configured in the pom.xml. Thereare plugins available that can manipulate the database, run SQL, and plugins likethe Maven Hibernate3 plugin which can generate annotated model objects for usein persistence frameworks. A few of these plugins, can be configured in a pom.xml

using these properties. These properties could also be used to filter resources. Inthis example, because we've defined a profile in ~/.m2/settings.xml which setsenvironment.type to dev, the development profile will always be activated whenwe run Maven on our development machine. Alternatively, if we wanted tooverride this default, we could set a property on the command-line. If we need toactivate the production profile, we could always run Maven with:

~/examples/profiles $ mvn install -Denvironment.type=prod

Setting a property on the command-line would override the default property set in~/.m2/settings.xml. We could have just defined a profile with an id of "dev" andinvoked it directly with the -P command-line argument, but using thisenvironment.type property allows us to code other project pom.xml files to thisstandard. Every project in your codebase could have a profile which is activated bythe same environment.type property set in every user's ~/.m2/settings.xml. Inthis way, developers can share common configuration for development withoutdefining this configuration in non-portable settings.xml files.

11.5.2. Protecting SecretsThis best practice builds upon the previous section. In Project Profile Activated byenvironment.type equalling 'dev', the production profile does not contain the

Build Profiles

276

Page 296: Maven Definitive Guide De

database.password property. I've done this on purpose to illustrate the concept ofputting secrets in you user-specific settings.xml. If you were developing anapplication at a large organization which values security, it is likely that themajority of the development group will not know the password to the productiondatabase. In an organization that draws a bold line between the development groupand the operations group, this will be the norm. Developers may have access to adevelopment and a staging environment, but they might not have (or want to have)access to the production database. There are a number of reasons why this makessense, particularly if an organization is dealing with extremely sensitive financial,intelligence, or medical information. In this scenario, the production environmentbuild may only be carried out by a lead developer or by a member of theproduction operations group. When they run this build using the prod

environment.type, they will need to define this variable in their settings.xml asfollows:

Example 11.8. Storing Secrets in a User-specific Settings Profile

<settings><profiles>

<profile><activeByDefault>true</activeByDefault><properties>

<environment.type>prod</environment.type><database.password>m1ss10nimp0ss1bl3</database.password>

</properties></profile>

</profiles></settings>

This user has defined a default profile which sets the environment.type to prod

and which also sets the production password. When the project is executed, theproduction profile is activated by the environment.type property and thedatabase.password property is populated. This way, you can put all of theproduction-specific configuration into a project's pom.xml and leave out only thesingle secret necessary to access the production database.

Note

Build Profiles

277

Page 297: Maven Definitive Guide De

Secrets usually conflict with wide portability, but this makes sense. Youwouldn't want to share your secrets openly.

11.5.3. Platform ClassifiersLet's assume that you have a library or a project that produces platform-specificcustomizations. Even though Java is platform-neutral, there are times when youmight need to write some code that invokes platform-specific native code. Anotherpossibility is that you've written some C code which is compiled by the MavenNative plugin and you want to produce a qualified artifact depending on the buildplatform. You can set a classifier with the Maven Assembly plugin or with theMaven Jar plugin. The following pom.xml produces a qualified artifact usingprofiles which are activated by Operation System parameters. For moreinformation about the Maven Assembly plugin, see Chapter 12, Maven Assemblies.

Example 11.9. Qualifying Artifacts with Platform Activated Project Profiles

<project>...<profiles>

<profile><id>windows</id><activation>

<os><family>windows</family>

</os></activation><build>

<plugins><plugin<artifactId>maven-jar-plugin</artifactId><configuration>

<classifier>win</classifier></configuration>

</plugin></plugins>

</build></profile><profile><id>linux</id><activation>

<os>

Build Profiles

278

Page 298: Maven Definitive Guide De

<family>unix</family></os>

</activation><build>

<plugins><plugin><artifactId>maven-jar-plugin</artifactId><configuration>

<classifier>linux</classifier></configuration>

</plugin></plugins>

</build></profile>

</profiles></project>

If the Operating System is in the Windows family, this pom.xml qualifies the JARartifact with "-win". If the Operating System is in the Unix family, the artifact isqualified with "-linux". This pom.xml successfully adds the qualifiers to theartifacts, but it is more verbose than it need to be due to the redundantconfiguration of the Maven Jar plugin in both profiles. This example could berewritten to use variable substitution to minimize redundancy as follows:

Example 11.10. Qualifying Artifacts with Platform Activated Project Profilesand Variable Substitution

<project>...<build>

<plugins><plugin>

<artifactId>maven-jar-plugin</artifactId><configuration>

<classifier>${envClassifier}</classifier></configuration>

</plugin></plugins>

</build>...<profiles>

<profile><id>windows</id><activation>

<os><family>windows</family>

Build Profiles

279

Page 299: Maven Definitive Guide De

</os></activation><properties>

<envClassifier>win</envClassifier></properties>

</profile><profile><id>linux</id><activation>

<os><family>unix</family>

</os></activation><properties>

<envClassifier>linux</envClassifier></properties>

</profile></profiles>

</project>

In this pom.xml, each profile doesn't need to include a build element to configurethe Jar plugin. Instead, each profile is activated by the Operating System familyand sets the envClassifier property to either win or linux. This envClassifieris then referenced in the default pom.xml build element to add a classifier to theproject's JAR artifact. The JAR artifact will be named${finalName}-${envClassifier}.jar and included as a dependency using thefollowing dependency syntax:

Example 11.11. Depending on a Qualified Artifact

<dependency><groupId>com.mycompany</groupId><artifactId>my-project</artifactId><version>1.0</version><classifier>linux</classifier>

</dependency>

11.6. SummaryWhen used judiciously, profiles can make it very easy to customize a build for

Build Profiles

280

Page 300: Maven Definitive Guide De

different platforms. If something in your build needs to define a platform-specificpath for something like an application server, you can put these configurationpoints in a profile which is activated by an operating system parameter. If you havea project which needs to produce different artifacts for different environments, youcan customize the build behavior for different environments and platforms viaprofile-specific plugin behavior. Using profiles, builds can become portable, thereis no need to rewrite your build logic to support a new environment, just overridethe configuration that needs to change and share the configuration points whichcan be shared.

Build Profiles

281

Page 301: Maven Definitive Guide De

Chapter 12. Maven Assemblies

12.1. IntroductionMaven provides plugins that are used to create the most common archive types,most of which are consumable as dependencies of other projects. Some examplesinclude the JAR, WAR, EJB, and EAR plugins. As discussed in Chapter 10, DerBuild Lebenszyklus these plugins correspond to different project packaging typeseach with a slightly different build process. While Maven has plugins andcustomized lifecycles to support standard packaging types, there are times whenyou'll need to create an archive or directory with a custom layout. Such customarchives are called Maven Assemblies.

There are any number of reasons why you may want to build custom archives foryour project. Perhaps the most common is the project distribution. The word‘distribution’ means many different things to different people (and projects),depending on how the project is meant to be used. Essentially, these are archivesthat provide a convenient way for users to install or otherwise make use of theproject’s releases. In some cases, this may mean bundling a web application withan application server like Jetty. In others, it could mean bundling APIdocumentation alongside source and compiled binaries like jar files. Assembliesusually come in handy when you are building the final distribution of a product.For example, products like Nexus introduced in Chapter 16, RepositoryManagement with Nexus, are the product of large multi-module Maven products,and the final archive you download from Sonatype was created using a MavenAssembly.

In most cases, the Assembly plugin is ideally suited to the process of buildingproject distributions. However, assemblies don’t have to be distribution archives;assemblies are intended to provide Maven users with the flexibility they need toproduce customized archives of all kinds. Essentially, assemblies are intended tofill the gaps between the standard archive formats provided by project packagetypes. Of course, you could write an entire Maven plugin simply to generate your

282

Page 302: Maven Definitive Guide De

own custom archive format, along with a new lifecycle mapping andartifact-handling configuration to tell Maven how to deploy it. But the Assemblyplugin makes this unnecessary in most cases by providing generalized support forcreating your own archive recipe without spending so much time writing Mavencode.

12.2. Assembly BasicsBefore we go any further, it’s best to take a minute and talk about the two maingoals in the Assembly plugin: assembly:assembly, and the single mojo. I listthese two goals in different ways because it reflects the difference in how they’reused. The assembly:assembly goal is designed to be invoked directly from thecommand line, and should never be bound to a build lifecycle phase. In contrast,the single mojo is designed to be a part of your everyday build, and should bebound to a phase in your project’s build lifecycle.

The main reason for this difference is that the assembly:assembly goal is whatMaven terms an aggregator mojo; that is, a mojo which is designed to run at mostonce in a build, regardless of how many projects are being built. It draws itsconfiguration from the root project - usually the top-level POM or the commandline. When bound to a lifecycle, an aggregator mojo can have some nastyside-effects. It can force the execution of the package lifecycle phase to executeahead of time, and can result in builds which end up executing the package phasetwice.

Because the assembly:assembly goal is an aggregator mojo, it raises some issuesin multi-module Maven builds, and it should only be called as a stand-alone mojofrom the command-line. Never bind an assembly:assembly execution to alifecycle phase. assembly:assembly was the original goal in the Assembly plugin,and was never designed to be part of the standard build process for a project. As itbecame clear that assembly archives were a legitimate requirement for projects toproduce, the single mojo was developed. This mojo assumes that it has beenbound to the correct part of the build process, so that it will have access to theproject files and artifacts it needs to execute within the lifecycle of a largemulti-module Maven project. In a multi-module environment, it will execute as

Maven Assemblies

283

Page 303: Maven Definitive Guide De

many times as it is bound to the different module POMs. Unlikeassembly:assembly, single will never force the execution of another lifecyclephase ahead of itself.

The Assembly plugin provides several other goals in addition to these two.However, discussion of these other mojos is beyond the scope of this chapter,because they serve exotic or obsolete use cases, and because they are almost neverneeded. Whenever possible, you should definitely stick to usingassembly:assembly for assemblies generated from the command line, and tosingle for assemblies bound to lifecycle phases.

12.2.1. Predefined Assembly DescriptorsWhile many people opt to create their own archive recipes - called assemblydescriptors - this isn’t strictly necessary. The Assembly plugin provides built-indescriptors for several common archive types that you can use immediatelywithout writing a line of configuration. The following assembly descriptors arepredefined in the Maven Assembly plugin:

bin

The bin descriptor is used to bundle project LICENSE, README, and NOTICE fileswith the project’s main artifact, assuming this project builds a jar as its mainartifact. Think of this as the smallest possible binary distribution for completelyself-contained projects.

jar-with-dependencies

The jar-with-dependencies descriptor builds a JAR archive with the contentsof the main project jar along with the unpacked contents of all the project’sruntime dependencies. Coupled with an appropriate Main-Class Manifest entry(discussed in “Plugin Configuration” below), this descriptor can produce aself-contained, executable jar for your project, even if the project hasdependencies.

project

The project descriptor simply archives the project directory structure as it

Maven Assemblies

284

Page 304: Maven Definitive Guide De

exists in your file-system and, most likely, in your version control system. Ofcourse, the target directory is omitted, as are any version-control metadata fileslike the CVS and .svn directories we’re all used to seeing. Basically, the point ofthis descriptor is to create a project archive that, when unpacked, can be builtusing Maven.

src

The src descriptor produces an archive of your project source and pom.xml

files, along with any LICENSE, README, and NOTICE files that are in the project’sroot directory. This precursor to the project descriptor produces an archive thatcan be built by Maven in most cases. However, because of its assumption thatall source files and resources reside in the standard src directory, it has thepotential to leave out non-standard directories and files that are nonethelesscritical to some builds.

12.2.2. Building an AssemblyThe Assembly plugin can be executed in two ways: you can invoke it directly fromthe command line, or you can configure it as part of your standard build process bybinding it to a phase of your project’s build lifecycle. Direct invocation has itsuses, particularly for one-off assemblies that are not considered part of yourproject’s core deliverables. In most cases, you’ll probably want to generate theassemblies for your project as part of its standard build process. Doing this has theeffect of including your custom assemblies whenever the project is installed ordeployed into Maven’s repositories, so they are always available to your users.

As an example of the direct invocation of the Assembly plugin, imagine that youwanted to ship off a copy of your project which people could build from source.Instead of just deploying the end-product of the build, you wanted to include thesource as well. You won’t need to do this often, so it doesn’t make sense to add theconfiguration to your POM. Instead, you can use the following command:

$ mvn -DdescriptorId=project assembly:single...[INFO] [assembly:single][INFO] Building tar : /Users/~/mvn-examples-1.0/assemblies/direct-invocation/\

target/direct-invocation-1.0-SNAPSHOT-project.tar.gz[INFO] Building tar : /Users/~/mvn-examples-1.0/assemblies/direct-invocation/\

Maven Assemblies

285

Page 305: Maven Definitive Guide De

target/direct-invocation-1.0-SNAPSHOT-project.tar.bz2[INFO] Building zip: /Users/~/mvn-examples-1.0/assemblies/direct-invocation/\

target/direct-invocation-1.0-SNAPSHOT-project.zip...

Imagine you want to produce an executable JAR from your project. If your projectis totally self-contained with no dependencies, this can be achieved with the mainproject artifact using the archive configuration of the JAR plugin. However, mostprojects have dependencies, and those dependencies must be incorporated in anyexecutable JAR. In this case, you want to make sure that every time the mainproject JAR is installed or deployed, your executable JAR goes along with it.

Assuming the main class for the project is org.sonatype.mavenbook.App, thefollowing POM configuration will create an executable JAR:

Example 12.1. Assembly Descriptor for Executable JAR

<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/maven-v4_0_0.xsd">

<modelVersion>4.0.0</modelVersion><groupId>org.sonatype.mavenbook.assemblies</groupId><artifactId>executable-jar</artifactId><version>1.0-SNAPSHOT</version><packaging>jar</packaging><name>Assemblies Executable Jar Example</name><url>http://sonatype.com/book</url><dependencies>

<dependency><groupId>commons-lang</groupId><artifactId>commons-lang</artifactId><version>2.4</version>

</dependency></dependencies>

<build><plugins><plugin>

<artifactId>maven-assembly-plugin</artifactId><version>2.2-beta-2</version><executions>

<execution><id>create-executable-jar</id><phase>package</phase><goals>

<goal>single</goal>

Maven Assemblies

286

Page 306: Maven Definitive Guide De

</goals><configuration>

<descriptorRefs><descriptorRef>jar-with-dependencies

</descriptorRef></descriptorRefs><archive><manifest><mainClass>org.sonatype.mavenbook.App</mainClass>

</manifest></archive>

</configuration></execution>

</executions></plugin>

</plugins></build>

</project>

There are two things to notice about the configuration above. First, we’re using thedescriptorRefs configuration section instead of the descriptorId parameter weused last time. This allows multiple assembly types to be built from the sameAssembly plugin execution, while still supporting our use case with relatively littleextra configuration. Second, the archive element under configuration sets theMain-Class manifest attribute in the generated JAR. This section is commonlyavailable in plugins that create JAR files, such as the JAR plugin used for thedefault project package type.

Now, you can produce the executable JAR simply by executing mvn package.Afterward, we’ll also get a directory listing for the target directory, just to verifythat the executable JAR was generated. Finally, just to prove that we actually dohave an executable JAR, we’ll try executing it:

$ mvn package... (output omitted) ...[INFO] [jar:jar][INFO] Building jar: ~/mvn-examples-1.0/assemblies/executable-jar/target/\

executable-jar-1.0-SNAPSHOT.jar[INFO] [assembly:single {execution: create-executable-jar}][INFO] Processing DependencySet (output=)[INFO] Building jar: ~/mvn-examples-1.0/assemblies/executable-jar/target/\

executable-jar-1.0-SNAPSHOT-jar-with-dependencies.jar... (output omitted) ...$ ls -1 target

Maven Assemblies

287

Page 307: Maven Definitive Guide De

... (output omitted) ...executable-jar-1.0-SNAPSHOT-jar-with-dependencies.jarexecutable-jar-1.0-SNAPSHOT.jar... (output omitted) ...$ java -jar \

target/executable-jar-1.0-SNAPSHOT-jar-with-dependencies.jarHello, World!

From the output shown above, you can see that the normal project build nowproduces a new artifact in addition to the main JAR file. The new one has aclassifier of jar-with-dependencies. Finally, we verified that the new JARactually is executable, and that executing the JAR produced the desired output of“Hello, World!”

12.2.3. Assemblies as DependenciesWhen you generate assemblies as part of your normal build process, thoseassembly archives will be attached to your main project’s artifact. This means theywill be installed and deployed alongside the main artifact, and are then resolvablein much the same way. Each assembly artifact is given the same basic coordinate(groupId, artifactId, and version) as the main project. However, these artifactsare attachments, which in Maven means they are derivative works based on someaspect of the main project build. To provide a couple of examples, sourceassemblies contain the raw inputs for the project build, andjar-with-dependencies assemblies contain the project’s classes plus itsdependencies. Attached artifacts are allowed to circumvent the Maven requirementof one project, one artifact precisely because of this derivative quality.

Since assemblies are (normally) attached artifacts, each must have a classifier todistinguish it from the main artifact, in addition to the normal artifact coordinate.By default, the classifier is the same as the assembly descriptor’s identifier. Whenusing the built-in assembly descriptors, as above, the assembly descriptor’sidentifier is generally also the same as the identifier used in the descriptorRef forthat type of assembly.

Once you’ve deployed an assembly alongside your main project artifact, how canyou use that assembly as a dependency in another project? The answer is fairlystraightforward. Recall the discussions in Section 3.5.3, “Maven Koordinaten” and

Maven Assemblies

288

Page 308: Maven Definitive Guide De

Section 9.5.1, “More on Coordinates” about project dependencies in Maven,projects depend on other projects using a combination of four basic elements,referred to as a project’s coordinates: groupId, artifactId, version, andpackaging. In Section 11.5.3, “Platform Classifiers”, multiple platform-specificvariants of a project’s artifact and available, and the project specifies a classifier

element with a value of either win or linux to select the appropriate dependencyartifact for the target platform. Assembly artifacts can be used as dependenciesusing the required coordinates of a project plus the classifier under which theassembly was installed or deployed. If the assembly is not a JAR archive, we alsoneed to declare its type.

12.2.4. Assembling Assemblies via AssemblyDependenciesHow's that for a confusing section title? Let's try to set up a scenario which wouldexplain the idea of assembling assemblies. Imagine you want to create an archivewhich itself contains some project assemblies. Assume that you have amulti-module build and you want to deploy an assembly which contains a set ofrelated project assemblies. In this section's example, we create a bundle of"buildable" project directories for a set of projects that are commonly usedtogether. For simplicity, we’ll reuse the two built-in assembly descriptorsdiscussed above - project and jar-with-dependencies. In this particularexample, it is assumed that each project creates the project assembly in additionto its main JAR artifact. Assume that every project in a multi-module build bindsthe single goal to the package phase and uses the project descriptorRef. Everyproject in a multi-module will inherit the configuration from a top-level pom.xmlwhose pluginManagement element is shown in Example 12.2, “Configuring theproject assembly in top-level POM”.

Example 12.2. Configuring the project assembly in top-level POM

<project>...<build>

<pluginManagement><plugins>

Maven Assemblies

289

Page 309: Maven Definitive Guide De

<plugin><artifactId>maven-assembly-plugin</artifactId><version>2.2-beta-2</version><executions><execution>

<id>create-project-bundle</id><phase>package</phase><goals><goal>single</goal>

</goals><configuration><descriptorRefs><descriptorRef>project</descriptorRef>

</descriptorRefs></configuration>

</execution></executions>

</plugin></plugins>

</pluginManagement></build>...

</project>

Each project POM references the managed plugin configuration fromExample 12.2, “Configuring the project assembly in top-level POM” using aminimal plugin declaration in its build section shown in Example 12.3, “Activatingthe Assembly Plugin Configuration in Child Projects”.

Example 12.3. Activating the Assembly Plugin Configuration in ChildProjects

<build><plugins>

<plugin><artifactId>maven-assembly-plugin</artifactId>

</plugin></plugins>

</build>

To produce the set of project assemblies, run mvn install from the top-leveldirectory. You should see Maven installing artifacts with classifiers in your localrepository.

Maven Assemblies

290

Page 310: Maven Definitive Guide De

$ mvn install...Installing ~/mvn-examples-1.0/assemblies/as-dependencies/project-parent/\

second-project/target/second-project-1.0-SNAPSHOT-project.tar.gz to~/.m2/repository/org/sonatype/mavenbook/assemblies/second-project/1.0-SNAPSHOT/\

second-project-1.0-SNAPSHOT-project.tar.gz...Installing ~/mvn-examples-1.0/assemblies/as-dependencies/project-parent/\

second-project/target/second-project-1.0-SNAPSHOT-project.tar.bz2 to~/.m2/repository/org/sonatype/mavenbook/assemblies/second-project/1.0-SNAPSHOT/\

second-project-1.0-SNAPSHOT-project.tar.bz2...Installing ~/mvn-examples-1.0/assemblies/as-dependencies/project-parent/\

second-project/target/second-project-1.0-SNAPSHOT-project.zip to~/.m2/repository/org/sonatype/mavenbook/assemblies/second-project/1.0-SNAPSHOT/\\

second-project-1.0-SNAPSHOT-project.zip...

When you run install, Maven will copy the each project's main artifact and eachassembly to your local Maven repository. All of these artifacts are now availablefor reference as dependencies in other projects locally. If your ultimate goal is tocreate a bundle which includes assemblies from multiple project, you can do so bycreating another project which will include other project's assemblies asdependencies. This bundling project (aptly named project-bundle) is responsiblefor creating the bundled assembly. The POM for the bundling project wouldresemble the XML document listed in Example 12.4, “POM for the AssemblyBundling Project”.

Example 12.4. POM for the Assembly Bundling Project

<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/maven-v4_0_0.xsd"><modelVersion>4.0.0</modelVersion><groupId>org.sonatype.mavenbook.assemblies</groupId><artifactId>project-bundle</artifactId><version>1.0-SNAPSHOT</version><packaging>pom</packaging><name>Assemblies-as-Dependencies Example Project Bundle</name><url>http://sonatype.com/book</url><dependencies>

<dependency><groupId>org.sonatype.mavenbook.assemblies</groupId><artifactId>first-project</artifactId><version>1.0-SNAPSHOT</version>

Maven Assemblies

291

Page 311: Maven Definitive Guide De

<classifier>project</classifier><type>zip</type>

</dependency><dependency><groupId>org.sonatype.mavenbook.assemblies</groupId><artifactId>second-project</artifactId><version>1.0-SNAPSHOT</version><classifier>project</classifier><type>zip</type>

</dependency></dependencies><build>

<plugins><plugin>

<artifactId>maven-assembly-plugin</artifactId><version>2.2-beta-2</version><executions>

<execution><id>bundle-project-sources</id><phase>package</phase><goals>

<goal>single</goal></goals><configuration>

<descriptorRefs><descriptorRef>jar-with-dependencies

</descriptorRef></descriptorRefs>

</configuration></execution>

</executions></plugin>

</plugins></build>

</project>

This bundling project's POM references the two assemblies from first-project

and second-project. Instead of referencing the main artifact of each project, thebundling project's POM specifies a classifier of project and a type of zip. Thistells Maven to resolve the ZIP archive which was created by the project

assembly. Note that the bundling project generates a jar-with-dependencies

assembly. jar-with-dependencies does not create a particularly elegant bundle, itsimply creates a JAR file with the unpacked contents of all of the dependencies.jar-with-dependencies is really just telling Maven to take all of thedependencies, unpack them, and then create a single archive which includes the

Maven Assemblies

292

Page 312: Maven Definitive Guide De

output of the current project. In this project, it has the effect of creating a singleJAR file that puts the two project assemblies from first-project andsecond-project side-by-side.

This example illustrates how the basic capabilities of the Maven Assembly plugincan be combined without the need for a custom assembly descriptor. It achievesthe purpose of creating a single archive that contains the project directories formultiple projects side-by-side. This time, the jar-with-dependencies is just astorage format, so we don’t need to specify a Main-Class manifest attribute. Tobuild the bundle, we just build the project-bundle project normally:

$ mvn package...[INFO] [assembly:single {execution: bundle-project-sources}][INFO] Processing DependencySet (output=)[INFO] Building jar: ~/downloads/mvn-examples-1.0/assemblies/as-dependencies/\

project-bundle/target/project-bundle-1.0-SNAPSHOT-jar-with-dependencies.jar

To verify that the project-bundle assembly contains the unpacked contents of theassembly dependencies, run jar tf:

$ jar tf \target/project-bundle-1.0-SNAPSHOT-jar-with-dependencies.jar

...first-project-1.0-SNAPSHOT/pom.xmlfirst-project-1.0-SNAPSHOT/src/main/java/org/sonatype/mavenbook/App.javafirst-project-1.0-SNAPSHOT/src/test/java/org/sonatype/mavenbook/AppTest.java...second-project-1.0-SNAPSHOT/pom.xmlsecond-project-1.0-SNAPSHOT/src/main/java/org/sonatype/mavenbook/App.javasecond-project-1.0-SNAPSHOT/src/test/java/org/sonatype/mavenbook/AppTest.java

After reading this section, the title should make more sense. You've assembledassemblies from two projects into an assembly using a bundling project which hasa dependency on each of the assemblies.

12.3. Overview of the Assembly DescriptorWhen the standard assembly descriptors introduced in Section 12.2, “AssemblyBasics” are not adequate, you will need to define your own assembly descriptor.The assembly descriptor is an XML document which defines the structure and

Maven Assemblies

293

Page 313: Maven Definitive Guide De

contents of an assembly.

Figure 12.1. Assembly Descriptor Picture

The assembly descriptor contains five main configuration sections, plus twoadditional sections: one for specifying standard assembly-descriptor fragments,called component descriptors, and another for specifying custom file processorclasses to help manage the assembly-production process.

Base ConfigurationThis section contains the information required by all assemblies, plus someadditional configuration options related to the format of the entire archive, suchas the base path to use for all archive entries. For the assembly descriptor to bevalid, you must at least specify the assembly id, at least one format, and at least

Maven Assemblies

294

Page 314: Maven Definitive Guide De

one of the other sections shown above.

File InformationThe configurations in this segment of the assembly descriptor apply to specificfiles on the file system within the project’s directory structure. This segmentcontains two main sections: files and fileSets. You use files and fileSets

to control the permissions of files in an assembly and to include or exclude filesfrom an assembly.

Dependency InformationAlmost all projects of any size depend on other projects. When creatingdistribution archives, project dependencies are usually included in theend-product of an assembly. This section manages the way dependencies areincluded in the resulting archive. This section allows you to specify whetherdependencies are unpacked, added directly to the lib/ directory, or mapped tonew file names. This section also allows you to control the permissions ofdependencies in the assembly, and which dependencies are included in anassembly.

Repository InformationAt times, it’s useful to isolate the sum total of all artifacts necessary to build aproject, whether they’re dependency artifacts, POMs of dependency artifacts, oreven a project’s own POM ancestry (your parent POM, its parent, and so on).This section allows you to include one or more artifact-repository directorystructures inside your assembly, with various configuration options. TheAssembly plugin does not have the ability to include plugin artifacts in theserepositories yet.

Module InformationThis section of the assembly descriptor allows you to take advantage of theseparent-child relationships when assembling your custom archive, to includesource files, artifacts, and dependencies from your project’s modules. This isthe most complex section of the assembly descriptor, because it allows you towork with modules and sub-modules in two ways: as a series of fileSets (viathe sources section) or as a series of dependencySets (via the binaries

Maven Assemblies

295

Page 315: Maven Definitive Guide De

section).

12.4. The Assembly DescriptorThis section is a tour of the assembly descriptor which contains some guidelinesfor developing a custom assembly descriptor. The Assembly plugin is one of thelargest plugins in the Maven ensemble, and one of the most flexible.

12.4.1. Property References in Assembly DescriptorsAny property discussed in Section 13.2, “Maven Properties” can be referenced inan assembly descriptor. Before any assembly descriptor is used by Maven, it isinterpolated using information from the POM and the current build environment.All properties supported for interpolation within the POM itself are valid for use inassembly descriptors, including POM properties, POM element values, systemproperties, user-defined properties, and operating-system environment variables.

The only exceptions to this interpolation step are elements in various sections ofthe descriptor named outputDirectory, outputDirectoryMapping, oroutputFileNameMapping. The reason these are held back in their raw form is toallow artifact- or module-specific information to be applied when resolvingexpressions in these values, on a per-item basis.

12.4.2. Required Assembly InformationThere are two essential pieces of information that are required for every assembly:the id, and the list of archive formats to produce. In practice, at least one othersection of the descriptor is required - since most archive format components willchoke if they don’t have at least one file to include - but without at least oneformat and an id, there is no archive to create. The id is used both in the archive’sfile name, and as part of the archive’s artifact classifier in the Maven repository.The format string also controls the archiver-component instance that will create thefinal assembly archive. All assembly descriptors must contain an id and at leastone format:

Maven Assemblies

296

Page 316: Maven Definitive Guide De

Example 12.5. Required Assembly Descriptor Elements

<assembly><id>bundle</id><formats>

<format>zip</format></formats>...

</assembly>

The assembly id can be any string that does not contain spaces. The standardpractice is to use dashes when you must separate words within the assembly id. Ifyou were creating an assembly to create an interesting unique package structure,you would give your an id of something like interesting-unique-package. Italso supports multiple formats within a single assembly descriptor, allowing you tocreate the familiar .zip, .tar.gz, and .tar.bz2 distribution archive set with ease.If you don't find the archive format you need, you can also create a custom format.Custom formats are discussed in Section 12.5.8, “componentDescriptors andcontainerDescriptorHandlers”. The Assembly plugin supports several archiveformats natively, including:

• jar

• zip

• tar

• bzip2

• gzip

• tar.gz

• tar.bz2

• rar

Maven Assemblies

297

Page 317: Maven Definitive Guide De

• war

• ear

• sar

• dir

The id and format are essential because they will become a part of the coordinatesfor the assembled archive. The example from Example 12.5, “Required AssemblyDescriptor Elements” will create an assembly artifact of type zip with a classifierof bundle.

12.5. Controlling the Contents of an AssemblyIn theory, id and format are the only absolute requirements for a valid assemblydescriptor; however, many assembly archivers will fail if they do not have at leastone file to include in the output archive. The task of defining the files to beincluded in the assembly is handled by the five main sections of the assemblydescriptor: files, fileSets, dependencySets, repositories, and moduleSets. Toexplore these sections most effectively, we’ll start by discussing the mostelemental section: files. Then, we’ll move on to the two most commonly usedsections, fileSets and dependencySets. Once you understand the workings offileSets and dependencySets, it’s easier to understand repositories andmoduleSets.

12.5.1. Files SectionThe files section is the simplest part of the assembly descriptor, it is designed forfiles that have a definite location relative to your project’s directory. Using thissection, you have absolute control over the exact set of files that are included inyour assembly, exactly what they are named, and where they will reside in thearchive.

Maven Assemblies

298

Page 318: Maven Definitive Guide De

Example 12.6. Including a JAR file in an Assembly using files

<assembly>...<files>

<file><source>target/my-app-1.0.jar</source><outputDirectory>lib</outputDirectory><destName>my-app.jar</destName><fileMode>0644</fileMode>

</file></files>...

</assembly>

Assuming you were building a project called my-app with a version of 1.0,Example 12.6, “Including a JAR file in an Assembly using files” would includeyour project's JAR in the assembly’s lib/ directory, trimming the version from thefile name in the process so the final file name is simply my-app.jar. It would thenmake the JAR readable by everyone and writable by the user that owns it (this iswhat the mode 0644 means for files, using Unix four-digit Octal permissionnotation). For more information about the format of the value in fileMode, pleasesee the Wikipedia's explanation of four-digit Octal notation.

You could build a very complex assembly using file entries, if you knew the fulllist of files to be included. Even if you didn’t know the full list before the buildstarted, you could probably use a custom Maven plugin to discover that list andgenerate the assembly descriptor using references like the one above. While thefiles section gives you fine-grained control over the permission, location, and nameof each file in the assembly archive, listing a file element for every file in a largearchive would be a tedious exercise. For the most part, you will be operating ongroups of files and dependencies using fileSets. The remaining fourfile-inclusion sections are designed to help you include entire sets of files thatmatch a particular criteria.

12.5.2. FileSets SectionSimilar to the files section, fileSets are intended for files that have a definite

Maven Assemblies

299

Page 319: Maven Definitive Guide De

location relative to your project’s directory structure. However, unlike the files

section, fileSets describe sets of files, defined by file and path patterns theymatch (or don’t match), and the general directory structure in which they arelocated. The simplest fileSet just specifies the directory where the files arelocated:

<assembly>...<fileSets>

<fileSet><directory>src/main/java</directory>

</fileSet></fileSets>...

</assembly>

This file set simply includes the contents of the src/main/java directory from ourproject. It takes advantage of many default settings in the section, so let’s discussthose briefly.

First, you’ll notice that we haven’t told the file set where within the assemblymatching files should be located. By default, the destination directory (specifiedwith outputDirectory) is the same as the source directory (in our case,src/main/java). Additionally, we haven’t specified any inclusion or exclusion filepatterns. When these are empty, the file set assumes that all files within the sourcedirectory are included, with some important exceptions. The exceptions to this rulepertain mainly to source-control metadata files and directories, and are controlledby the useDefaultExcludes flag, which is defaulted to true. When active,useDefaultExcludes will keep directories like .svn/ and CVS/ from being addedto the assembly archive. Section 12.5.3, “Default Exclusion Patterns for fileSets”provides a detailed list of the default exclusion patterns.

If we want more control over this file set, we can specify it more explicitly.Example 12.7, “Including Files with fileSet” shows a fileSet element with allof the default elements specified.

Example 12.7. Including Files with fileSet

<assembly>...<fileSets>

Maven Assemblies

300

Page 320: Maven Definitive Guide De

<fileSet><directory>src/main/java</directory><outputDirectory>src/main/java</outputDirectory><includes>

<include>**</include></includes><useDefaultExcludes>true</useDefaultExcludes><fileMode>0644</fileMode><directoryMode>0755</directoryMode>

</fileSet></fileSets>...

</assembly>

The includes section uses a list of include elements, which contain path patterns.These patterns may contain wildcards such as ‘**’ which matches one or moredirectories or ‘*’ which matches part of a file name, and ‘?’ which matches a singlecharacter in a file name. Example 12.7, “Including Files with fileSet” uses afileMode entry to specify that files in this set should be readable by all, but onlywritable by the owner. Since the fileSet includes directories, we also have theoption of specifying a directoryMode that works in much the same way as thefileMode. Since a directories’ execute permission is what allows users to list theircontents, we want to make sure directories are executable in addition to beingreadable. Like files, only the owner can write to directories in this set.

The fileSet entry offers some other options as well. First, it allows for anexcludes section with a form identical to the includes section. These exclusionpatterns allow you to exclude specific file patterns from a fileSet. Includepatterns take precedence over exclude patterns. Additionally, you can set thefiltering flag to true if you want to substitute property values for expressionswithin the included files. Expressions can be delimited either by ${ and } (standardMaven expressions like ${project.groupId}) or by @ and @ (standard Antexpressions like @project.groupId@). You can adjust the line ending of your filesusing the lineEnding element; valid values for lineEnding are:

keepPreserve line endings from original files. (This is the default value.)

Maven Assemblies

301

Page 321: Maven Definitive Guide De

unixUnix-style line endings

lfOnly a Line Feed Character

dosMS-DOS-style line endings

crlfCarriage-return followed by a Line Feed

Finally, if you want to ensure that all file-matching patterns are used, you can usethe useStrictFiltering element with a value of true (the default is false). Thiscan be especially useful if unused patterns may signal missing files in anintermediary output directory. When useStrictFiltering is set to true, theAssembly plugin will fail if an include pattern is not satisfied. In other words, ifyou have an include pattern which includes a file from a build, and that file is notpresent, setting useStrictFiltering to true will cause a failure if Maven cannotfind the file to be included.

12.5.3. Default Exclusion Patterns for fileSetsWhen you use the default exclusion patterns, the Maven Assembly plugin is goingto be ignoring more than just SVN and CVS information. By default the exclusionpatterns are defined by the DirectoryScanner class in the plexus-utils project hostedat Codehaus. The array of exclude patterns is defined as a static, final String arraynamed DEFAULTEXCLUDES in DirectoryScanner. The contents of this variable areshown in Example 12.8, “Definition of Default Exclusion Patterns from PlexusUtils”.

Example 12.8. Definition of Default Exclusion Patterns from Plexus Utils

public static final String[] DEFAULTEXCLUDES = {// Miscellaneous typical temporary files"**/*~","**/#*#",

Maven Assemblies

302

Page 322: Maven Definitive Guide De

"**/.#*","**/%*%","**/._*",

// CVS"**/CVS","**/CVS/**","**/.cvsignore",

// SCCS"**/SCCS","**/SCCS/**",

// Visual SourceSafe"**/vssver.scc",

// Subversion"**/.svn","**/.svn/**",

// Arch"**/.arch-ids","**/.arch-ids/**",

//Bazaar"**/.bzr","**/.bzr/**",

//SurroundSCM"**/.MySCMServerInfo",

// Mac"**/.DS_Store"

};

This default array of patterns excludes temporary files from editors like GNUEmacs, and other common temporary files from Macs and a few common sourcecontrol systems (although Visual SourceSafe is more of a curse than a sourcecontrol system). If you need to override these default exclusion patterns you setuseDefaultExcludes to false and then define a set of exclusion patterns in yourown assembly descriptor.

12.5.4. dependencySets SectionOne of the most common requirements for assemblies is the inclusion of a

Maven Assemblies

303

Page 323: Maven Definitive Guide De

project’s dependencies in an assembly archive. Where files and fileSets dealwith files in your project, dependency files don't have a location in your project.The artifacts your project depends on have to be resolved by Maven during thebuild. Dependency artifacts are abstract, they lack a definite location, and areresolved using a symbolic set of Maven coordinates. While Since file andfileSet specifications require a concrete source path, dependencies are includedor excluded from an assembly using a combination of Maven coordinates anddependency scopes.

The simplest dependencySet is an empty element:

<assembly>...<dependencySets>

<dependencySet/></dependencySets>...

</assembly>

The dependencySet above will match all runtime dependencies of your project(runtime scope includes the compile scope implicitly), and it will add thesedependencies to the root directory of your assembly archive. It will also copy thecurrent project’s main artifact into the root of the assembly archive, if it exists.

NoteWait? I thought dependencySet was about including my project'sdependencies, not my project's main archive? This counterintuitiveside-effect was a widely-used bug in the 2.1 version of the Assemblyplugin, and, because Maven puts an emphasis on backward compatibility,this counterintuitive and incorrect behavior needed to be preservedbetween a 2.1 and 2.2 release. You can control this behavior by changingthe useProjectArtifact flag to false.

While the default dependency set can be quite useful with no configurationwhatsoever, this section of the assembly descriptor also supports a wide array ofconfiguration options, allowing your to tailor its behavior to your specificrequirements. For example, the first thing you might do to the dependency set

Maven Assemblies

304

Page 324: Maven Definitive Guide De

above is exclude the current project artifact, by setting the useProjectArtifact

flag to false (again, its default value is true for legacy reasons). This will allowyou to manage the current project’s build output separately from its dependencyfiles. Alternatively, you might choose to unpack the dependency artifacts using bysetting the unpack flag to true (this is false by default). When unpack is set totrue, the Assembly plugin will combine the unpacked contents of all matchingdependencies inside the archive’s root directory.

From this point, there are several things you might choose to do with thisdependency set. The next sections discuss how to define the output location fordependency sets and how include and exclude dependencies by scope. Finally,we’ll expand on the unpacking functionality of the dependency set by exploringsome advanced options for unpacking dependencies.

12.5.4.1. Customizing Dependency Output LocationThere are two configuration options that are used in concert to define the locationfor a dependency file within the assembly archive: outputDirectory andoutputFileNameMapping. You may want to customize the location ofdependencies in your assembly using properties of the dependency artifactsthemselves. Let's say you want to put all the dependencies in directories that matchthe dependency artifact's groupId. In this case, you would use theoutputDirectory element of the dependencySet, and you would supplysomething like:

<assembly>...<dependencySets>

<dependencySet><outputDirectory>${artifact.groupId}</outputDirectory>

</dependencySet></dependencySets>...

</assembly>

This would have the effect of placing every single dependency in a subdirectorythat matched the name of each dependency artifact's groupId.

If you wanted to perform a further customization and remove the version numbersfrom all dependencies. You could customize the the output file name for each

Maven Assemblies

305

Page 325: Maven Definitive Guide De

dependency using the outputFileNameMapping element as follows:

<assembly>...<dependencySets>

<dependencySet><outputDirectory>${artifact.groupId}</outputDirectory><outputFileNameMapping>

${artifact.artifactId}.${artifact.extension}</outputFileNameMapping>

</dependencySet></dependencySets>...

</assembly>

In the previous example, a dependency on commons:commons-codec version 1.3,would end up in the file commons/commons-codec.jar.

12.5.4.2. Interpolation of Properties in Dependency Output LocationAs mentioned in the Assembly Interpolation section above, neither of theseelements are interpolated with the rest of the assembly descriptor, because theirraw values have to be interpreted using additional, artifact-specific expressionresolvers.

The artifact expressions available for these two elements vary only slightly. In bothcases, all of the ${project.*}, ${pom.*}, and ${*} expressions that are availablein the POM and the rest of the assembly descriptor are also available here. For theoutputFileNameMapping element, the following process is applied to resolveexpressions:

1. If the expression matches the pattern ${artifact.*}:

a. Match against the dependency’s Artifact instance (resolves: groupId,artifactId, version, baseVersion, scope, classifier, and file.*)

b. Match against the dependency’s ArtifactHandler instance (resolves:expression)

c. Match against the project instance associated with the dependency’sArtifact (resolves: mainly POM properties)

Maven Assemblies

306

Page 326: Maven Definitive Guide De

2. If the expression matches the patterns ${pom.*} or ${project.*}:

a. Match against the project instance (MavenProject) of the current build.

3. If the expression matches the pattern ${dashClassifier?} and the Artifactinstance contains a non-null classifier, resolve to the classifier preceded by adash (-classifier). Otherwise, resolve to an empty string.

4. Attempt to resolve the expression against the project instance of the currentbuild.

5. Attempt to resolve the expression against the POM properties of the currentbuild.

6. Attempt to resolve the expression against the available system properties.

7. Attempt to resolve the expression against the available operating-systemenvironment variables.

The outputDirectory value is interpolated in much the same way, with thedifference being that there is no available ${artifact.*} information, only the${project.*} instance for the particular artifact. Therefore, the expressions listedabove associated with those classes (1a, 1b, and 3 in the process listing above) areunavailable.

How do you know when to use outputDirectory and outputFileNameMapping?When dependencies are unpacked only the outputDirectory is used to calculatethe output location. When dependencies are managed as whole files (notunpacked), both outputDirectory and outputFileNameMapping can be usedtogether. When used together, the result is the equivalent of:

<archive-root-dir>/<outputDirectory>/<outputFileNameMapping>

When outputDirectory is missing, it is not used. When outputFileNameMapping

is missing, its default value is:${artifact.artifactId}-${artifact.version}${dashClassifier?}.${artifact.extension}

Maven Assemblies

307

Page 327: Maven Definitive Guide De

12.5.4.3. Including and Excluding Dependencies by ScopeIn Section 9.4, “Project Dependencies”, it was noted that all project dependencieshave one scope or another. Scope determines when in the build process thatdependency normally would be used. For instance, test-scoped dependencies arenot included in the classpath during compilation of the main project sources; butthey are included in the classpath when compiling unit test sources. This is becauseyour project’s main source code should not contain any code specific to testing,since testing is not a function of the project (it’s a function of the project’s buildprocess). Similarly, provided-scoped dependencies are assumed to be present in theenvironment of any eventual deployment. However, if a project depends on aparticular provided dependency, it is likely to require that dependency in order tocompile. Therefore, provided-scoped dependencies are present in the compilationclasspath, but not in the dependency set that should be bundled with the project’sartifact or assembly.

Also from Section 9.4, “Project Dependencies”, recall that some dependencyscopes imply others. For instance, the runtime dependency scope implies thecompile scope, since all compile-time dependencies (except for those in theprovided scope) will be required for the code to execute. There are a number ofcomplex relationships between the various dependency scopes which control howthe scope of a direct dependency affects the scope of a transitive dependency. In aMaven Assembly descriptor, we can use scopes to apply different settings todifferent sets of dependencies accordingly.

For instance, if we plan to bundle a web application with Jetty to create acompletely self-contained application, we’ll need to include all provided-scopedependencies somewhere in the jetty directory structure we’re including. Thisensures those provided dependencies actually are present in the runtimeenvironment. Non-provided, runtime dependencies will still land in theWEB-INF/lib directory, so these two dependency sets must be processedseparately. These dependency sets might look similar to the following XML.

Example 12.9. Defining Dependency Sets Using Scope

<assembly>

Maven Assemblies

308

Page 328: Maven Definitive Guide De

...<dependencySets>

<dependencySet><scope>provided</scope><outputDirectory>lib/${project.artifactId}</outputDirectory>

</dependencySet><dependencySet><scope>runtime</scope><outputDirectory>

webapps/${webContextName}/WEB-INF/lib</outputDirectory>

</dependencySet></dependencySets>...

</assembly>

Provided-scoped dependencies are added to the lib/ directory in the assemblyroot, which is assumed to be a libraries directory that will be included in the Jettyglobal runtime classpath. We’re using a subdirectory named for the project’sartifactId in order to make it easier to track the origin of a particular library.Runtime dependencies are included in the WEB-INF/lib path of the webapplication, which is located within a subdirectory of the standard Jetty webapps/

directory that is named using a custom POM property called webContextName.What we've done in the previous example is separate application-specificdependencies from dependencies which will be present in a Servlet contains globalclasspath.

However, simply separating according to scope may not be enough, particularly inthe case of a web application. It’s conceivable that one or more runtimedependencies will actually be bundles of standardized, non-compiled resources foruse in the web application. For example, consider a set of web application whichreuse a common set of Javascript, CSS, SWF, and image resources. To make theseresources easy to standardize, it’s a common practice to bundle them up in anarchive and deploy them to the Maven repository. At that point, they can bereferenced as standard Maven dependencies - possibly with a dependency type ofzip - that are normally specified with a runtime scope. Remember, these areresources, not binary dependencies of the application code itself; therefore, it’s notappropriate to blindly include them in the WEB-INF/lib directory. Instead, theseresource archives should be separated from binary runtime dependencies, and

Maven Assemblies

309

Page 329: Maven Definitive Guide De

unpacked into the web application document root somewhere. In order to achievethis kind of separation, we’ll need to use inclusion and exclusion patterns thatapply to the coordinates of a specific dependency.

In other words, say you have three or four web application which reuse the sameresources and you want to create an assembly that puts provided dependencies intolib/, runtime dependencies into webapps/<contextName>/WEB-INF/lib, and thenunpacks a specific runtime dependency into your web application's document root.You can do this because the Assembly allows you to define multiple include andexclude patterns for a given dependencySet element. Read the next section formore development of this idea.

12.5.4.4. Fine Tuning: Dependency Includes and ExcludesA resource dependency might be as simple as a set of resources (CSS, Javascript,and Images) in a project that has an assembly which creates a ZIP archive.Depending on the particulars of our web application, we might be able todistinguish resource dependencies from binary dependencies solely according totype. Most web applications are going to depend on other dependencies of typejar, and it is possible that we can state with certainty that all dependencies of typezip are resource dependencies. Or, we might have a situation where resources arestored in jar format, but have a classifier of something like resources. In eithercase, we can specify an inclusion pattern to target these resource dependencies andapply different logic than that used for binary dependencies. We’ll specify thesetuning patterns using the includes and excludes sections of the dependencySet.

Both includes and excludes are list sections, meaning they accept the sub-elementsinclude and exclude respectively. Each include or exclude element contains astring value, which can contain wildcards. Each string value can matchdependencies in a few different ways. Generally speaking, three identity patternformats are supported:

groupId:artifactId - version-less keyYou would use this pattern to match a dependency by only the groupId and theartifactId

Maven Assemblies

310

Page 330: Maven Definitive Guide De

groupId:artifactId:type[:classifier] - conflict idThe pattern allows you to specify a wider set of coordinates to create a morespecific include/exclude pattern.

groupId:artifactId:type[:classifier]:version - full artifact identityIf you need to get really specific, you can specify all the coordinates.

All of these pattern formats support the wildcard character ‘*’, which can matchany subsection of the identity and is not limited to matching single identity parts(sections between ‘:’ characters). Also, note that the classifier section above isoptional, in that patterns matching dependencies that don’t have classifiers do notneed to account for the classifier section in the pattern.

In the example given above, where the key distinction is the artifact type zip, andnone of the dependencies have classifiers, the following pattern would matchresource dependencies assuming that they were of type zip:

*:zip

The pattern above makes use of the second dependency identity: the dependency’sconflict id. Now that we have a pattern that distinguishes resource dependenciesfrom binary dependencies, we can modify our dependency sets to handle resourcearchives differently:

Example 12.10. Using Dependency Excludes and Includes in dependencySets

<assembly>...<dependencySets>

<dependencySet><scope>provided</scope><outputDirectory>lib/${project.artifactId}</outputDirectory>

</dependencySet><dependencySet><scope>runtime</scope><outputDirectory>

webapps/${webContextName}/WEB-INF/lib</outputDirectory><excludes>

<exclude>*:zip</exclude></excludes>

</dependencySet>

Maven Assemblies

311

Page 331: Maven Definitive Guide De

<dependencySet><scope>runtime</scope><outputDirectory>

webapps/${webContextName}/resources</outputDirectory><includes>

<include>*:zip</include></includes><unpack>true</unpack>

</dependencySet></dependencySets>...

</assembly>

In Example 12.10, “Using Dependency Excludes and Includes independencySets”, the runtime-scoped dependency set from our last example hasbeen updated to exclude resource dependencies. Only binary dependencies(non-zip dependencies) should be added to the WEB-INF/lib directory of the webapplication. Resource dependencies now have their own dependency set, which isconfigured to include these dependencies in the resources directory of the webapplication. The includes section in the last dependencySet reverses the exclusionfrom the previous dependencySet, so that resource dependencies are includedusing the same identity pattern (i.e. *:zip). The last dependencySet refers to theshared resource dependency and it is configured to unpack the shared resourcedependency in the document root of the web application.

Example 12.10, “Using Dependency Excludes and Includes in dependencySets”was based upon the assumption that our shared resources project dependency had atype which differed from all of the other dependencies. What if the share resourcedependency had the same type as all of the other dependencies? How could youdifferentiate the dependency? In this case if the shared resource dependency hadbeen bundled as a JAR with the classifier resources, you can change to theidentity pattern and match those dependencies instead:

*:jar:resources

Instead of matching on artifacts with a type of zip and no classifier, we’rematching on artifacts with a classifier of resources and a type of jar.

Just like the fileSets section, dependencySets support the useStrictFiltering

Maven Assemblies

312

Page 332: Maven Definitive Guide De

flag. When enabled, any specified patterns that don’t match one or moredependencies will cause the assembly - and consequently, the build - to fail. Thiscan be particularly useful as a safety valve, to make sure your project dependenciesand assembly descriptors are synchronized and interacting as you expect them to.By default, this flag is set to false for the purposes of backward compatibility.

12.5.4.5. Transitive Dependencies, Project Attachments, and ProjectArtifactsThe dependencySet section supports two more general mechanisms for tuning thesubset of matching artifacts: transitive selection options, and options for workingwith project artifacts. Both of these features are a product of the need to supportlegacy configurations that applied a somewhat more liberal definition of the word“dependency”. As a prime example, consider the project’s own main artifact.Typically, this would not be considered a dependency; yet older versions of theAssembly plugin included the project artifact in calculations of dependency sets.To provide backward compatibility with this “feature”, the 2.2 releases (currentlyat 2.2-beta-2) of the Assembly plugin support a flag in the dependencySet calleduseProjectArtifact, whose default value is true. By default, dependency setswill attempt to include the project artifact itself in calculations about whichdependency artifacts match and which don’t. If you’d rather deal with the projectartifact separately, set this flag to false.

TipThe authors of this book recommend that you always setuseProjectArtifact to false.

As a natural extension to the inclusion of the project artifact, the project’s attachedartifacts can also be managed within a dependencySet using theuseProjectAttachments flag (whose default value is false). Enabling this flagallows patterns that specify classifiers and types to match on artifacts that are“attached” to the main project artifact; that is, they share the same basicgroupId/artifactId/version identity, but differ in type and classifier from themain artifact. This could be useful for including JavaDoc or source jars in an

Maven Assemblies

313

Page 333: Maven Definitive Guide De

assembly.

Aside from dealing with the project’s own artifacts, it’s also possible to fine-tunethe dependency set using two transitive-resolution flags. The first, calleduseTransitiveDependencies (and set to true by default) simply specifies whetherthe dependency set should consider transitive dependencies at all whendetermining the matching artifact set to be included. As an example of how thiscould be used, consider what happens when your POM has a dependency onanother assembly. That assembly (most likely) will have a classifier that separatesit from the main project artifact, making it an attachment. However, one quirk ofthe Maven dependency-resolution process is that the transitive-dependencyinformation for the main artifact is still used when resolving the assembly artifact.If the assembly bundles its project dependencies inside itself, using transitivedependency resolution here would effectively duplicate those dependencies. Toavoid this, we simply set useTransitiveDependencies to false for thedependency set that handles that assembly dependency.

The other transitive-resolution flag is far more subtle. It’s calleduseTransitiveFiltering, and has a default value of false. To understand whatthis flag does, we first need to understand what information is available for anygiven artifact during the resolution process. When an artifact is a dependency of adependency (that is, removed at least one level from your own POM), it has whatMaven calls a "dependency trail", which is maintained as a list of strings thatcorrespond to the full artifact identities(groupId:artifactId:type:[classifier:]version) of all dependencies betweenyour POM and the artifact that owns that dependency trail. If you remember thethree types of artifact identities available for pattern matching in a dependency set,you’ll notice that the entries in the dependency trail - the full artifact identity -correspond to the third type. When useTransitiveFiltering is set to true, theentries in an artifact’s dependency trail can cause the artifact to be included orexcluded in the same way its own identity can.

If you’re considering using transitive filtering, be careful! A given artifact can beincluded from multiple places in the transitive-dependency graph, but as of Maven2.0.9, only the first inclusion’s trail will be tracked for this type of matching. Thiscan lead to subtle problems when collecting the dependencies for your project.

Maven Assemblies

314

Page 334: Maven Definitive Guide De

WarningMost assemblies don’t really need this level of control over dependencysets; consider carefully whether yours truly does. Hint: It probablydoesn't.

12.5.4.6. Advanced Unpacking OptionsAs we discussed previously, some project dependencies may need to be unpackedin order to create a working assembly archive. In the examples above, the decisionto unpack or not was simple. It didn’t take into account what needed to beunpacked, or more importantly, what should not be unpacked. To gain morecontrol over the dependency unpacking process, we can configure theunpackOptions element of the dependencySet. Using this section, we have theability to choose which file patterns to include or exclude from the assembly, andwhether included files should be filtered to resolve expressions using current POMinformation. In fact, the options available for unpacking dependency sets are fairlysimilar to those available for including files from the project directory structure,using the file sets descriptor section.

To continue our web-application example, suppose some of the resourcedependencies have been bundled with a file that details their distribution license. Inthe case of our web application, we’ll handle third-party license notices by way ofa NOTICES file included in our own bundle, so we don’t want to include the licensefile from the resource dependency. To exclude this file, we simply add it to theunpack options inside the dependency set that handles resource artifacts:

Example 12.11. Excluding Files from a Dependency Unpack

<asembly>...<dependencySets>

<dependencySet><scope>runtime</scope><outputDirectory>

webapps/${webContextName}/resources</outputDirectory><includes>

<include>*:zip</include>

Maven Assemblies

315

Page 335: Maven Definitive Guide De

</includes><unpack>true</unpack><unpackOptions>

<excludes><exclude>**/LICENSE*</exclude>

</excludes></unpackOptions>

</dependencySet></dependencySets>...

</assembly>

Notice that the exclude we’re using looks very similar to those used in fileSet

declarations. Here, we’re blocking any file starting with the word LICENSE in anydirectory within our resource artifacts. You can think of the unpack options sectionas a lightweight fileSet applied to each dependency matched within thatdependency set. In other words, it is a fileSet by way of an unpackeddependency. Just as we specified an exclusion pattern for files within resourcedependencies in order to block certain files, you can also choose which restrictedset of files to include using the includes section. The same code that processesinclusions and exclusions on fileSets has been reused for processingunpackOptions.

In addition to file inclusion and exclusion, the unpack options on a dependency setalso provides a filtering flag, whose default value is false. Again, this shouldbe familiar from our discussion of file sets above. In both cases, expressions usingeither the Maven syntax of ${property} or the Ant syntax of @property@ aresupported. Filtering is a particularly nice feature to have for dependency sets,though, since it effectively allows you to create standardized, versioned resourcetemplates that are then customized to each assembly as they are included. Onceyou start mastering the use of filtered, unpacked dependencies which store sharedresources, you will be able to start abstracting repeated resources into commonresource projects.

12.5.4.7. Summarizing Dependency SetsFinally, it’s worth mentioning that dependency sets support the same fileMode anddirectoryMode configuration options that file sets do, though you should

Maven Assemblies

316

Page 336: Maven Definitive Guide De

remember that the directoryMode setting will only be used when dependencies areunpacked.

12.5.5. moduleSets SectionsMulti-module builds are generally stitched together using the parent and modulessections of interrelated POMs. Typically, parent POMs specify their children in amodules section, which under normal circumstances causes the child POMs to beincluded in the build process of the parent. Exactly how this relationship isconstructed can have important implications for the ways in which the Assemblyplugin can participate in this process, but we’ll discuss that more later. For now,it’s enough to keep in mind this parent-module relationship as we discuss themoduleSets section.

Projects are stitched together into multi-module builds because they are part of alarger system. These projects are designed to be used together, and single modulein a larger build has little practical value on its own. In this way, the structure ofthe project’s build is related to the way we expect the project (and its modules) tobe used. If consider the project from the user's perspective, it makes sense that theideal end goal of that build would be a single, distributable file that the user canconsume directly with minimum installation hassle. Since Maven multi-modulebuilds typically follow a top-down structure, where dependency information,plugin configurations, and other information trickles down from parent to child, itseems natural that the task of rolling all of these modules into a single distributionfile should fall to the topmost project. This is where the moduleSet comes into thepicture.

Module sets allow the inclusion of resources that belong to each module in theproject structure into the final assembly archive. Just like you can select a group offiles to include in an assembly using a fileSet and a dependencySet, you caninclude a set of files and resources using a moduleSet to refer to modules in amulti-module build. They achieve this by enabling two basic types ofmodule-specific inclusion: file-based, and artifact-based. Before we get into thespecifics and differences between file-based and artifact-based inclusion of moduleresources into an assembly, let’s talk a little about selecting which modules to

Maven Assemblies

317

Page 337: Maven Definitive Guide De

process.

12.5.5.1. Module SelectionBy now, you should be familiar with includes/excludes patterns as they are usedthroughout the assembly descriptor to filter files and dependencies. When you arereferring to modules in an assembly descriptor, you will also use theincludes/excludes patterns to define rules which apply to different sets ofmodules. The difference in moduleSet includes and excludes is that these rulesdo not allow for wildcard patterns. (As of the 2.2-beta-2 release, this feature hasnot really seen much demand, so it hasn’t been implemented.) Instead, eachinclude or exclude value is simply the groupId and artifactId for the module,separated by a colon, like this:

groupId:artifactId

In addition to includes and excludes, the moduleSet also supports an additionalselection tool: the includeSubModules flag (whose default value is true). Theparent-child relationship in any multi-module build structure is not strictly limitedto two tiers of projects. In fact, you can include any number of tiers, or layers, inyour build. Any project that is a module of a module of the current project isconsidered a sub-module. In some cases, you may want to deal with eachindividual module in the build separately (including sub-modules). For example,this is often simplest when dealing with artifact-based contributions from thesemodules. To do this, you would simply leave the useSubModules flag set to thedefault of true.

When you’re trying to include files from each module’s directory structure, youmay wish to process that module’s directory structure only once. If your projectdirectory structure mirrors that of the parent-module relationships that are includedin the POMs, this approach would allow file patterns like **/src/main/java to applynot only to that direct module’s project directory, but also to the directories of itsown modules as well. In this case you don’t want to process sub-modules directly(they will be processed as subdirectories within your own project’s modulesinstead), you should set the useSubModules flag to false.

Once we’ve determined how module selection should proceed for the module set

Maven Assemblies

318

Page 338: Maven Definitive Guide De

in question, we’re ready to choose what to include from each module. Asmentioned above, this can include files or artifacts from the module project.

12.5.5.2. Sources SectionSuppose you want to include the source of all modules in your project's assembly,but you would like to exclude a particular module. Maybe you have a projectnamed secret-sauce which contains secret and sensitive code that you don't wantto distribute with your project. The simplest way to accomplish this is to use amoduleSet which includes each project's directory in ${module.basedir.name}

and which excludes the secret-sauce module from the assembly.

Example 12.12. Includes and Excluding Modules with a moduleSet

<assembly>...<moduleSets>

<moduleSet><includeSubModules>false</includeSubModules><excludes>

<exclude>com.mycompany.application:secret-sauce

</exclude></excludes><sources>

<outputDirectoryMapping>${module.basedir.name}

</outputDirectoryMapping><excludeSubModuleDirectories>

false</excludeSubModuleDirectories><fileSets>

<fileSet><directory>/</directory><excludes>

<exclude>**/target</exclude></excludes>

</fileSet></fileSets>

</sources></moduleSet>

</moduleSets>...

</assembly>

Maven Assemblies

319

Page 339: Maven Definitive Guide De

In Example 12.12, “Includes and Excluding Modules with a moduleSet”, sincewe’re dealing with each module’s sources it’s simpler to deal only with directmodules of the current project, handling sub-modules using file-path wildcardpatterns in the file set. We set the includeSubModules element to false so wedon't have to worry about submodules showing up in the root directory of theassembly archive. The exclude element will take care of excluding thesecret-sauce module. We’re not going to include the project sources for thesecret-sauce module; they’re, well, secret.

Normally, module sources are included in the assembly under a subdirectorynamed after the module’s artifactId. However, since Maven allows modules thatare not in directories named after the module project’s artifactId, it’s often betterto use the expression ${module.basedir.name} to preserve the module directory’sactual name (${module.basedir.name} is the same as callingMavenProject.getBasedir().getName()). It is critical to remember that modulesare not required to be subdirectories of the project that declares them. If yourproject has a particularly strange directory structure, you may need to resort tospecial moduleSet declarations that include specific project and account for yourown project's idiosyncracies.

WarningTry to minimize your own project's idiosyncracies, while Maven isflexible, if you find yourself doing too much configuration there is likelyan easier way.

Continuing through Example 12.12, “Includes and Excluding Modules with amoduleSet”, since we’re not processing sub-modules explicitly in this module set,we need to make sure sub-module directories are not excluded from the sourcedirectories we consider for each direct module. By setting theexcludeSubModuleDirectories flag to false, this allows us to apply the same filepattern to directory structures within a sub-module of the one we’re processing.Finally in Example 12.12, “Includes and Excluding Modules with a moduleSet”,we’re not interested in any output of the build process for this module set. Weexclude the target/ directory from all modules.

Maven Assemblies

320

Page 340: Maven Definitive Guide De

It’s also worth mentioning that the sources section supports fileSet-like elementsdirectly within itself, in addition to supporting nested fileSets. Theseconfiguration elements are used to provide backward compatibility to previousversions of the Assembly plugin (versions 2.1 and under) that didn’t supportmultiple distinct file sets for the same module without creating a separate moduleset declaration. They are deprecated, and should not be used.

12.5.5.3. Interpolation of outputDirectoryMapping in moduleSets

In Section 12.5.4.1, “Customizing Dependency Output Location”, we used theelement outputDirectoryMapping to change the name of the directory underwhich each module’s sources would be included. The expressions contained in thiselement are resolved in exactly the same way as the outputFileNameMapping, usedin dependency sets (see the explanation of this algorithm in Section 12.5.4,“dependencySets Section”).

In Example 12.12, “Includes and Excluding Modules with a moduleSet”, we usedthe expression ${module.basedir.name}. You might notice that the root of thatexpression, module, is not listed in the mapping-resolution algorithm from thedependency sets section; this object root is specific to configurations withinmoduleSets. It works in exactly the same way as the ${artifact.*} referencesavailable in the outputFileNameMapping element, except it is applied to themodule’s MavenProject, Artifact, and ArtifactHandler instances instead ofthose from a dependency artifact.

12.5.5.4. Binaries sectionJust as the sources section is primarily concerned with including a module in itssource form, the binaries section is primarily concerned with including themodule’s build output, or its artifacts. Though this section functions primarily as away of specifying dependencySets that apply to each module in the set, there are afew additional features unique to module artifacts that are worth exploring:attachmentClassifier and includeDependencies. In addition, the binaries

section contains options similar to the dependencySet section, that relate to thehandling of the module artifact itself. These are: unpack, outputFileNameMapping,outputDirectory, directoryMode, and fileMode. Finally, module binaries can

Maven Assemblies

321

Page 341: Maven Definitive Guide De

contain a dependencySets section, to specify how each module’s dependenciesshould be included in the assembly archive. First, let’s take a look at how theoptions mentioned here can be used to manage the module’s own artifacts.

Suppose we want to include the javadoc jars for each of our modules inside ourassembly. In this case, we don’t care about including the module dependencies; wejust want the javadoc jar. However, since this particular jar is always going to bepresent as an attachment to the main project artifact, we need to specify whichclassifier to use to retrieve it. For simplicity, we won’t cover unpacking the modulejavadoc jars, since this configuration is exactly the same as what we used fordependency sets earlier in this chapter. The resulting module set might look similarto Example 12.13, “Including JavaDoc from Modules in an Assembly”.

Example 12.13. Including JavaDoc from Modules in an Assembly

<assembly>...<moduleSets>

<moduleSet><binaries>

<attachmentClassifier>javadoc</attachmentClassifier><includeDependencies>false</includeDependencies><outputDirectory>apidoc-jars</outputDirectory>

</binaries></moduleSet>

</moduleSets>...

</assembly>

In Example 12.13, “Including JavaDoc from Modules in an Assembly”, we don’texplicitly set the includeSubModules flag, since it’s true by default. However, wedefinitely want to process all modules - even sub-modules - using this module set,since we’re not using any sort of file pattern that could match on sub-moduledirectory structures within. The attachmentClassifier grabs the attached artifactwith the javadoc classifier for each module processed. The includeDependencies

element tells the Assembly plugin that we're not interested in any of the module'sdependencies, just the javadoc attachment. Finally, the outputDirectory elementtells the Assembly plugin to put all of the javadoc jars into a directory namedapidoc-jars/ off of the assembly root directory.

Maven Assemblies

322

Page 342: Maven Definitive Guide De

Although we’re not doing anything too complicated in this example, it’s importantto understand that the same changes to the expression-resolution algorithmdiscussed for the outputDirectoryMapping element of the sources section alsoapplies here. That is, whatever was available as ${artifact.*} inside adependencySet’s outputFileNameMapping configuration is also available here as${module.*}. The same applies for outputFileNameMapping when used directlywithin a binaries section.

Finally, let’s examine an example where we simply want to process the module’sartifact and its runtime dependencies. In this case, we want to separate the artifactset for each module into separate directory structures, according to the module’sartifactId and version. The resulting module set is surprisingly simply, and itlooks like the listing in Example 12.14, “Including Module Artifacts andDependencies in an Assembly”:

Example 12.14. Including Module Artifacts and Dependencies in an Assembly

<assembly>...<moduleSets>

<moduleSet><binaries>

<outputDirectory>${module.artifactId}-${module.version}

</outputDirectory><dependencySets>

<dependencySet/></dependencySets>

</binaries></moduleSet>

</moduleSets>...

</assembly>

In Example 12.14, “Including Module Artifacts and Dependencies in anAssembly”, we’re using the empty dependencySet element here, since that shouldinclude all runtime dependencies by default, with no configuration. With theoutputDirectory specified at the binaries level, all dependencies should beincluded alongside the module’s own artifact in the same directory, so we don’teven need to specify that in our dependency set.

Maven Assemblies

323

Page 343: Maven Definitive Guide De

For the most part, module binaries are fairly straightforward. In both parts - themain part, concerned with handling the module artifact itself, and the dependencysets, concerned with the module’s dependencies - the configuration options arevery similar to those in a dependency set. Of course, the binaries section alsoprovides options for controlling whether dependencies are included, and whichmain-project artifact you want to use.

Like the sources section, the binaries section contains a couple of configurationoptions that are provided solely for backward compatibility, and should beconsidered deprecated. These include the includes and excludes sub-sections.

12.5.5.5. moduleSets, Parent POMs and the binaries SectionFinally, we close the discussion about module handling with a strong warning.There are subtle interactions between Maven’s internal design as it relates toparent-module relationships and the execution of a module-set’s binaries section.When a POM declares a parent, that parent must be resolved in some way or otherbefore the POM in question can be built. If the parent is in the Maven repository,there is no problem. However, as of Maven 2.0.9 this can cause big problems ifthat parent is a higher-level POM in the same build, particularly if that parent POMexpects to build an assembly using its modules’ binaries.

Maven 2.0.9 sorts projects in a multi-module build according to theirdependencies, with a given project’s dependencies being built ahead of itself. Theproblem is the parent element is considered a dependency, which means the parentproject’s build must complete before the child project is built. If part of thatparent’s build process includes the creation of an assembly that uses modulebinaries, those binaries will not exist yet, and therefore cannot be included, causingthe assembly to fail. This is a complex and subtle issue, which severely limits theusefulness of the module binaries section of the assembly descriptor. In fact, it hasbeen filed in the bug tracker for the Assembly plugin at:http://jira.codehaus.org/browse/MASSEMBLY-97. Hopefully, future versions ofMaven will find a way to restore this functionality, since the parent-firstrequirement may not be completely necessary.

Maven Assemblies

324

Page 344: Maven Definitive Guide De

12.5.6. Repositories SectionThe repositories section represents a slightly more exotic feature in the assemblydescriptor, since few applications other than Maven can take full advantage of aMaven-repository directory structure. For this reason, and because many of itsfeatures closely resemble those in the dependencySets section, we won’t spend toomuch time on the repositories section of the assembly descriptor. In most cases,users who understand dependency sets should have no trouble constructingrepositories via the Assembly plugin. We're not going to motivate therepositories section; we're not going to go through a the business of setting up ause case and walking you through the process. We're just going to bring up a fewcaveats for those of you who find the need to use the repostiories section.

Having said that, there are a two features particular to the repositories section thatdeserve some mention. The first is the includeMetadata flag. When set to true itincludes metadata such as the list of real versions that correspond to -SNAPSHOT

virtual versions, and by default it’s set to false. At present, the only metadataincluded when this flag is true is the information downloaded from Maven’scentral repository.

The second feature is called groupVersionAlignments. Again, this section is a listof individual groupVersionAlignment configurations, whose purpose is tonormalize all included artifacts for a particular groupId to use a single version.Each alignment entry consists of two mandatory elements - id and version - alongwith an optional section called excludes that supplies a list of artifactId stringvalues which are to be excluded from this realignment. Unfortunately, thisrealignment doesn’t seem to modify the POMs involved in the repository, neitherthose related to realigned artifacts nor those that depend on realigned artifacts, soit’s difficult to imagine what the practical application for this sort of realignmentwould be.

In general, it’s simplest to apply the same principles you would use in dependencysets to repositories when adding them to your assembly descriptor. While therepositories section does support the above extra options, they are mainly providedfor backward compatibility, and will probably be deprecated in future releases.

Maven Assemblies

325

Page 345: Maven Definitive Guide De

12.5.7. Managing the Assembly’s Root DirectoryNow that we’ve made it through the main body of the assembly descriptor, we canclose the discussion of content-related descriptor sections with something lighter:root-directory naming and site-directory handling.

Some may consider it a stylistic concern, but it’s often important to have controlover the name of the root directory for your assembly, or whether the root directoryis there at all. Fortunately, two configuration options in the root of the assemblydescriptor make managing the archive root directory simple:includeBaseDirectory and baseDirectory. In cases like executable jar files, youprobably don’t want a root directory at all. To skip it, simply set theincludeBaseDirectory flag to false (it’s true by default). This will result in anarchive that, when unpacked, may create more than one directory in the unpacktarget directory. While this is considered bad form for archives that are meant to beunpacked before use, it’s not so bad for archives that are consumable as-is.

In other cases, you may want to guarantee the name of the archive root directoryregardless of the POM’s version or other information. By default, thebaseDirectory element has a value equal to${project.artifactId}-${project.version}. However, we can easily set thiselement to any value that consists of literal strings and expressions which can beinterpolated from the current POM, such as${project.groupId}-${project.artifactId}. This could be very good news foryour documentation team! (We all have those, right?)

Another configuration available is the includeSiteDirectory flag, whose defaultvalue is false. If your project build has also constructed a website document rootusing the site lifecycle or the Site plugin goals, that output can be included bysetting this flag to true. However, this feature is a bit limited, since it onlyincludes the outputDirectory from the reporting section of the current POM (bydefault, target/site) and doesn’t take into consideration any site directories thatmay be available in module projects. Use it if you want, but a good fileSet

specification or moduleSet specification with sources configured could serveequally well, if not better. This is yet another example of legacy configurationcurrently supported by the Assembly plugin for the purpose of backward

Maven Assemblies

326

Page 346: Maven Definitive Guide De

compatibility. Your mileage may vary. If you really want to include a site that isaggregated from many modules, you'll want to consider using a fileSet ormoduleSet instead of setting includeSiteDirectory to true.

12.5.8. componentDescriptors andcontainerDescriptorHandlers

To round out our exploration of the assembly descriptor, we should touch brieflyon two other sections: containerDescriptorHandlers andcomponentDescriptors. The containerDescriptorHandlers section refers tocustom components that you use to extend the capabilities of the Assembly plugin.Specifically, these custom components allow you to define and handle special fileswhich may need to be merged from the multiple constituents used to create yourassembly. A good example of this might be a custom container-descriptor handlerthat merged web.xml files from constituent war or war-fragment files included inyour assembly, in order to create the single web-application descriptor required foryou to use the resulting assembly archive as a war file.

The componentDescriptors section allows you to reference externalassembly-descriptor fragments and include them in the current descriptor.Component references can be any of the following:

1. Relative file paths: src/main/assembly/component.xml

2. Artifact references: groupId:artifactId:version[:type[:classifier]]

3. Classpath resources: /assemblies/component.xml

4. URLs: http://www.sonatype.com/component.xmlIncidentally, when resolving a component descriptor, the Assembly plugin triesthose different strategies in that exact order. The first one to succeed is used.

Component descriptors can contain many of the same content-oriented sectionsavailable in the assembly descriptor itself, with the exception of moduleSets,which is considered so specific to each project that it’s not a good candidate for

Maven Assemblies

327

Page 347: Maven Definitive Guide De

reuse. Also included in a component descriptor is thecontainerDescriptorHandlers section, which we briefly discussed above.Component descriptors cannot contain formats, assembly id’s, or any configurationrelated to the base directory of the assembly archive, all of which are alsoconsidered unique to a particular assembly descriptor. While it may make sense toallow sharing of the formats section, this has not been implemented as of the2.2-beta-2 Assembly-plugin release.

12.6. Best PracticesThe Assembly plugin provides enough flexibility to solve many problems in anumber of different ways. If you have a unique requirement for your project,there's a good chance that you can use the methods documented in this chapter toachieve almost any assembly structure. This section of the chapter details somecommon best practices which, if adhered to, will make your experiences with theassembly plugin more productive and less painful.

12.6.1. Standard, Reusable Assembly DescriptorsUp to now, we’ve been talking mainly about one-off solutions for building aparticular type of assembly. But what do you do if you have dozens of projects thatall need a particular type of assembly? In short, how can we reuse the effort we’veinvested to get our assemblies just the way we like them across more than oneproject without copying and pasting our assembly descriptor?

The simplest answer is to create a standardized, versioned artifact out of theassembly descriptor, and deploy it. Once that’s done, you can specify that theAssembly plugin section of your project’s POM include the assembly-descriptorartifact as a plugin-level dependency, which will prompt Maven to resolve andinclude that artifact in the plugin’s classpath. At that point, you can use theassembly descriptor via the descriptorRefs configuration section in the Assemblyplugin declaration. To illustrate, consider this example assembly descriptor:

<assembly><id>war-fragment</id><formats>

Maven Assemblies

328

Page 348: Maven Definitive Guide De

<format>zip</format></formats>

<includeBaseDirectory>false</includeBaseDirectory><dependencySets>

<dependencySet><outputDirectory>WEB-INF/lib</outputDirectory>

</dependencySet></dependencySets><fileSets>

<fileSet><directory>src/main/webapp</directory><outputDirectory>/</outputDirectory><excludes>

<exclude>**/web.xml</exclude></excludes>

</fileSet></fileSets>

</assembly>

Included in your project, this descriptor would be a useful way to bundle theproject contents so that it could be unpacked directly into an existing webapplication in order to add to it (for adding an extending feature, say). However, ifyour team builds more than one of these web-fragment projects, it will likely wantto reuse this descriptor rather than duplicating it. To deploy this descriptor as itsown artifact, we’re going to put it in its own project, under thesrc/main/resources/assemblies directory.

The project structure for this assembly-descriptor artifact will look similar to thefollowing:

|-- pom.xml`-- src

`-- main`-- resources

`-- assemblies`-- web-fragment.xml

Notice the path of our web-fragment descriptor file. By default, Maven includesthe files from the src/main/resources directory structure in the final jar, whichmeans our assembly descriptor will be included with no extra configuration on ourpart. Also, notice the assemblies/ path prefix, the Assembly plugin expects thispath prefix on all descriptors provided in the plugin classpath. It’s important thatwe put our descriptor in the appropriate relative location, so it will be picked up bythe Assembly plugin as it executes.

Maven Assemblies

329

Page 349: Maven Definitive Guide De

Remember, this project is separate from your actual web-fragment project now;the assembly descriptor has become its own artifact with its own version and,possibly, its own release cycle. Once you install this new project using Maven,you’ll be able to reference it in your web-fragment projects. For clarity, the buildprocess should look something like this:

$ mvn install(...)[INFO] [install:install][INFO] Installing (...)/web-fragment-descriptor/target/\

web-fragment-descriptor-1.0-SNAPSHOT.jarto /Users/~/.m2/repository/org/sonatype/mavenbook/assemblies/\

web-fragment-descriptor/1.0-SNAPSHOT/\web-fragment-descriptor-1.0-SNAPSHOT.jar

[INFO] ---------------------------------------------------------------[INFO] BUILD SUCCESSFUL[INFO] ---------------------------------------------------------------[INFO] Total time: 5 seconds(...)

Since there are no sources for the web-fragment-descriptor project, the resultingjar artifact will include nothing but our web-fragment assembly descriptor. Now,let’s use this new descriptor artifact:

<project>(...)<artifactId>my-web-fragment</artifactId>(...)<build>

<plugins><plugin>

<artifactId>maven-assembly-plugin</artifactId><version>2.2-beta-2</version><dependencies>

<dependency><groupId>org.sonatype.mavenbook.assemblies</groupId><artifactId>web-fragment-descriptor</artifactId><version>1.0-SNAPSHOT</version>

</dependency></dependencies><executions>

<execution><id>assemble</id><phase>package</phase><goals>

<goal>single</goal></goals><configuration>

<descriptorRefs>

Maven Assemblies

330

Page 350: Maven Definitive Guide De

<descriptorRef>web-fragment</descriptorRef></descriptorRefs>

</configuration></execution>

</executions></plugin>(...)

</plugins></build>(...)

</project>

Two things are special about this Assembly plugin configuration:

• We have to include a plugin-level dependency declaration on our newweb-fragment-descriptor artifact in order to have access to the assemblydescriptor via the plugin’s classpath.

• Since we’re using a classpath reference instead of a file in the local projectdirectory structure, we must use the descriptorRefs section instead of thedescriptor section. Also, notice that, while the assembly descriptor isactually in the assemblies/web-fragment.xml location within the plugin’sclasspath, we reference it without the assemblies/ prefix. This is becausethe Assembly plugin assumes that built-in assembly descriptors will alwaysreside in the classpath under this path prefix.

Now, you’re free to reuse the POM configuration above in as many projects as youlike, with the assurance that all of their web-fragment assemblies will turn out thesame. As you need to make adjustments to the assembly format - maybe to includeother resources, or to fine-tune the dependency and file sets - you can simplyincrement the version of the assembly descriptor’s project, and release it again.POMs referencing the assembly-descriptor artifact can then adopt this new versionof the descriptor as they are able.

One final point about assembly-descriptor reuse: you may want to consider sharingthe plugin configuration itself as well as publishing the descriptor as an artifact.This is a fairly simple step; you simply add the configuration listed above to thepluginManagement section of your parent POM, then reference the managed pluginconfiguration from your module POM like this:

(...)

Maven Assemblies

331

Page 351: Maven Definitive Guide De

<build><plugins><plugin>

<artifactId>maven-assembly-plugin</artifactId></plugin>

(...)

If you’ve added the rest of the plugin’s configuration - listed in the previousexample - to the pluginManagement section of the project’s parent POM, then eachproject inheriting from that parent POM can add a minimal entry like the oneabove and take advantage of an advanced assembly format in their own builds.

12.6.2. Distribution (Aggregating) AssembliesAs mentioned above, the Assembly plugin provides multiple ways of creatingmany archive formats. Distribution archives are typically very good examples ofthis, since they often combine modules from a multi-module build, along with theirdependencies and possibly, other files and artifacts besides these. The distributionaims to include all these different sources into a single archive that the user candownload, unpack, and run with convenience. However, we also examined some ofthe potential drawbacks of using the moduleSets section of the assembly descriptor- namely, that the parent-child relationships between POMs in a build can preventthe availability of module artifacts in some cases.

Specifically, if module POMs reference as their parent the POM that contains theAssembly-plugin configuration, that parent project will be built ahead of themodule projects when the multi-module build executes. The parent’s assemblyexpects to find artifacts in place for its modules, but these module projects arewaiting on the parent itself to finish building, a gridlock situation is reached andthe parent build cannot succeed (since it’s unable to find artifacts for its moduleprojects). In other words, the child project depends on the parent project which inturn depends on the child project.

As an example, consider the assembly descriptor below, designed to be used fromthe top-level project of a multi-module hierarchy:

<assembly><id>distribution</id><formats>

Maven Assemblies

332

Page 352: Maven Definitive Guide De

<format>zip</format><format>tar.gz</format><format>tar.bz2</format>

</formats>

<moduleSets><moduleSet><includes>

<include>*-web</include></includes><binaries>

<outputDirectory>/</outputDirectory><unpack>true</unpack><includeDependencies>true</includeDependencies><dependencySets>

<dependencySet><outputDirectory>/WEB-INF/lib</outputDirectory>

</dependencySet></dependencySets>

</binaries></moduleSet><moduleSet><includes>

<include>*-addons</include></includes><binaries>

<outputDirectory>/WEB-INF/lib</outputDirectory><includeDependencies>true</includeDependencies><dependencySets>

<dependencySet/></dependencySets>

</binaries></moduleSet>

</moduleSets></assembly>

Given a parent project - called app-parent - with three modules called app-core,app-web, and app-addons, notice what happens when we try to execute thismulti-module build:

$ mvn package[INFO] Reactor build order:[INFO] app-parent <----- PARENT BUILDS FIRST[INFO] app-core[INFO] app-web[INFO] app-addons[INFO] ---------------------------------------------------------------[INFO] Building app-parent[INFO] task-segment: [package][INFO] ---------------------------------------------------------------[INFO] [site:attach-descriptor]

Maven Assemblies

333

Page 353: Maven Definitive Guide De

[INFO] [assembly:single {execution: distro}][INFO] Reading assembly descriptor: src/main/assembly/distro.xml[INFO] ---------------------------------------------------------------[ERROR] BUILD ERROR[INFO] ---------------------------------------------------------------[INFO] Failed to create assembly: Artifact:org.sonatype.mavenbook.assemblies:app-web:jar:1.0-SNAPSHOT (included by module)does not have an artifact with a file. Please ensure the package phase isrun before the assembly is generated....

The parent project - app-parent - builds first. This is because each of the otherprojects lists that POM as its parent, which causes it to be forced to the front of thebuild order. The app-web module, which is the first module to be processed in theassembly descriptor, hasn’t been built yet. Therefore, it has no artifact associatedwith it, and the assembly cannot succeed.

One workaround for this is to remove the executions section of theAssembly-plugin declaration, that binds the plugin to the package lifecycle phasein the parent POM, keeping the configuration section intact. Then, execute Mavenwith two command-line tasks: the first, package, to build the multi-module projectgraph, and a second, assembly:assembly, as a direct invocation of the assemblyplugin to consume the artifacts built on the previous run, and create the distributionassembly. The command line for such a build might look like this:

$ mvn package assembly:assembly

However, this approach has several drawbacks. First, it makes thedistribution-assembly process more of a manual task that can increase thecomplexity and potential for error in the overall build process significantly.Additionally, it could mean that attached artifacts - which are associated inmemory as the project build executes - are not reachable on the second passwithout resorting to file-system references.

Instead of using a moduleSet to collect the artifacts from your multi-module build,it often makes more sense to employ a low-tech approach: using a dedicateddistribution project module and inter-project dependencies. In this approach, youcreate a new module in your build whose sole purpose is to assemble thedistribution. This module POM contains dependency references to all the othermodules in the project hierarchy, and it configures the Assembly plugin to be

Maven Assemblies

334

Page 354: Maven Definitive Guide De

bound the package phase of its build lifecycle. The assembly descriptor itself usesthe dependencySets section instead of the moduleSets section to collect moduleartifacts and determine where to include them in the resulting assembly archive.This approach escapes the pitfalls associated with the parent-child relationshipdiscussed above, and has the additional advantage of using a simpler configurationsection within the assembly descriptor itself to do the job.

To do this, we can create a new project structure that’s very similar to the one usedfor the module-set approach above, with the addition of a new distribution project,we might end up with five POMs in total: app-parent, app-core, app-web,app-addons, and app-distribution. The new app-distribution POM lookssimilar to the following:

<project><parent>

<artifactId>app-parent</artifactId><groupId>org.sonatype.mavenbook.assemblies</groupId><version>1.0-SNAPSHOT</version>

</parent><modelVersion>4.0.0</modelVersion><artifactId>app-distribution</artifactId><name>app-distribution</name>

<dependencies><dependency><artifactId>app-web</artifactId><groupId>org.sonatype.mavenbook.assemblies</groupId><version>1.0-SNAPSHOT</version><type>war</type>

</dependency><dependency><artifactId>app-addons</artifactId><groupId>org.sonatype.mavenbook.assemblies</groupId><version>1.0-SNAPSHOT</version>

</dependency><!-- Not necessary since it's brought in via app-web.<dependency> [2]<artifactId>app-core</artifactId><groupId>org.sonatype.mavenbook.assemblies</groupId><version>1.0-SNAPSHOT</version>

</dependency>-->

</dependencies></project>

Notice that we have to include dependencies for the other modules in the project

Maven Assemblies

335

Page 355: Maven Definitive Guide De

structure, since we don’t have a modules section to rely on in this POM. Also,notice that we’re not using an explicit dependency on app-core. Since it’s also adependency of app-web, we don’t need to process it (or, avoid processing it) twice.

Next, when we move the distro.xml assembly descriptor into theapp-distribution project, we must also change it to use a dependencySets

section, like this:

<assembly>...<dependencySets>

<dependencySet><includes>

<include>*-web</include></includes><useTransitiveDependencies>false</useTransitiveDependencies><outputDirectory>/</outputDirectory><unpack>true</unpack>

</dependencySet><dependencySet><excludes>

<exclude>*-web</exclude></excludes><useProjectArtifact>false</useProjectArtifact><outputDirectory>/WEB-INF/lib</outputDirectory>

</dependencySet></dependencySets>...

</assembly>

This time, if we run the build from the top-level project directory, we get betternews:

$ mvn package(...)[INFO] ---------------------------------------------------------------[INFO] Reactor Summary:[INFO] ---------------------------------------------------------------[INFO] module-set-distro-parent ...............SUCCESS [3.070s][INFO] app-core .............................. SUCCESS [2.970s][INFO] app-web ............................... SUCCESS [1.424s][INFO] app-addons ............................ SUCCESS [0.543s][INFO] app-distribution ...................... SUCCESS [2.603s][INFO] ---------------------------------------------------------------[INFO] ---------------------------------------------------------------[INFO] BUILD SUCCESSFUL[INFO] ---------------------------------------------------------------[INFO] Total time: 10 seconds[INFO] Finished at: Thu May 01 18:00:09 EDT 2008

Maven Assemblies

336

Page 356: Maven Definitive Guide De

[INFO] Final Memory: 16M/29M[INFO] ---------------------------------------------------------------

As you can see, the dependency-set approach is much more stable and - at leastuntil Maven’s internal project-sorting logic catches up with the Assembly plugin’scapabilities, - involves less opportunity to get things wrong when running a build.

12.7. SummaryAs we’ve seen in this chapter, the Maven Assembly plugin offers quite a bit ofpotential for creating custom archive formats. While the details of these assemblyarchives can be complex, they certainly don’t have to be in all cases - as we sawwith built-in assembly descriptors. Even if your aim is to include your project’sdependencies and selected project files in some unique, archived directorystructure, writing a custom assembly descriptor doesn’t have to be an arduous task.

Assemblies are useful for a wide array of applications, but are most commonlyused as application distributions of various sorts. And, while there are manydifferent ways to use the Assembly plugin, using standardized assembly-descriptorartifacts and avoiding moduleSets when creating distributions containing binariesare two sure ways to avoid problems.

Maven Assemblies

337

Page 357: Maven Definitive Guide De

Chapter 13. Properties and RessourceFilterung

13.1. EinleitungIn diesem Buch finden Sie durchgängig Hinweise auf Properties (Eigenschaften)welche Sie in POM-Dateien referenzieren können. Auf geerbte Abhängigkeiteneines multi-projekt Builds kann mittels der Properties ${project.groupId} sowie${project.version} zugegriffen werden und jeder Abschnitt eines POM kannreferenziert werden indem man einer Variablen die Vorsilbe „project.“ Anfügt.Umgebungsvariablen und Java System Einstellungen können referenziert werden,wie auch alle Einstellungen aus Ihrer ~/.m2/settings.xml Datei. Was Ihnen nochnicht auf den Weg gegeben wurde ist eine Aufstellung der möglichen PropertyVariablen, und eine Beschreibung wie diese zum Einsatz kommen um einenportablen Build zu erstellen. Dieses Kapitel nun liefert Ihnen diese Aufstellung.

Sollten Sie bereits Property Referenzen in Ihrem POM einsetzen, so sollte Ihnenebenfalls bewusst sein, dass Maven eine Funktionalität mit dem Namen RessourceFilter anbietet, welche Ihnen erlaubt, jegliche Property Referenz zu ändern, welchein einer Ressource Datei unter src/main/resources abgelegt ist. Standardmässigist diese Funktion deaktiviert, um unbeabsichtigte Veränderung von PropertyReferenzen zu verhindern. Diese Funktionalität kann jedoch Einsatz finden, umeinen Build auf eine bestimmte Plattform zu richten, oder um wichtige PropertyEinstellungen in Ressource Dateien, POMs oder Profilen zu externalisieren. DiesesKapitel hat zum Ziel, die Funktionalität des Ressource Filtering darzustellen undgibt eine kurze Einführung wie diese zum Einsatz kommt um einen portablenEnterprise Build herzustellen.

13.2. Maven PropertiesSie können Maven Properties in einer pom.xml Datei, oder irgendeiner anderen

338

Page 358: Maven Definitive Guide De

Ressource ablegen, welche vom Maven Ressource Plugin und dessen FilterFunktion verarbeitet wird. Ein Property ist immer umschlossen von ${ und } . Umauf das Property project.version zu referenzieren, würden Sie folgende Variablebenutzen:

${project.version}

Es bestehen einige implizite Maven Properties, diese sind:

project.*

Maven Project Objekt Modell (POM). Benutzen Sie das Präfix project.* umWerte des Maven POM zu referenzieren.

settings.*

Maven Settings; Benutzen Sie das Präfix settings.* um Werte aus der lokalenMaven Settings Datei (gewöhnlich unter ~/.m2/settings.xml zu referenzieren.

env.*

Umgebungsvariables wie z.B. PATH sowie M2_HOME können mittels dem Präfixenv.* benannte werden.

System PropertiesJeglicher Wert welcher durch die System.getProperty() Methode abgefragtwerden kann, kann als Maven Property referenziert werden.

Zusätzlich zu den impliziten Properties wie oben dargestellt, können das MavenPOM, die Maven Settings Datei sowie das Maven Profil weitere freidefinierteWerte enthalten. Der folgende Abschnitt wird Ihnen einigen Einblick in dieverschiedenen in einem Maven Projekt verfügbaren Werte geben.

13.2.1. Maven Projekt EinstellungenImmer wenn ein Maven Projekt Property referenziert wird, zeigt der Name desProperty auf einen Wert des Projekt Objekt Modell (POM). Genauer gesagt, Siesetzen eine Referenz auf ein Property der Klasse org.apache.maven.model.Model

welche als implizite Projektvariable dargestellt wird. Wollen Sie sich auf ein

Properties and Ressource Filterung

339

Page 359: Maven Definitive Guide De

Property dieser impliziten Variablen beziehen, so wenden Sie die einfachePunkt-Notation an, um auf ein Bean-Property des Modell-Objektes zu zeigen.Referenzieren Sie zum Beispiel ${project.version}, so rufen Sie tatsächlich dieMethode getVersion() der Instanz des Modell Objektes auf, als welches dasProjekt offengelegt wird.

Das POM wird ebenfalls durch die Datei pom.xml dargestellt, welche in allenMaven Projekten präsent ist. Alle Bestandteile des Maven POM können alsProperty angesprochen werden. Eine komplette Referenz der POM Struktur findenSie unter http://maven.apache.org/ref/2.0.9/maven-model/maven.html. In derfolgenden Liste werden wir einige der häufiger gebrauchten Referenzen auf MavenProjekt Properties ausführen:

project.groupId, project.versionProjekte die Bestandteil eines grossen multi-modularen Build sind, benutzenhäufig die selbe groupId und version Bezeichner. Sollten Sie Abhängigkeitenzwischen verschiedenen Modulen deklarieren welche die gleiche groupId undversion tragen, so tun Sie gut daran, für beide eine Property Referenz zudefinieren:

<dependencies><dependency>

<groupId>${project.groupId}</groupId><artifactId>sibling-project</artifactId><version>${project.version}</version>

</dependency></dependencies>

project.artifactId

Die artifactId eines Projektes wird oft als Name eines Paketes eingesetzt. Somöchten Sie, im Falle eines Projektes das als WAR paketiert wird, ein WARohne Versionsbezug im Namen generieren. Um dies zu bewerkstelligen würdenSie project.artifactId wie folgt referenzieren:

<build><finalName>${project.artifactId}</finalName>

</build>

Properties and Ressource Filterung

340

Page 360: Maven Definitive Guide De

project.name, project.description

Der Name und die Projektbeschreibung sind ofmals nützliche Referenzen ineiner Dokumentation. Anstatt sich damit abzumüchen, dass der gesamte Site diegleiche Kurzbeschreibung benutzt, ist es viel einfacher diese Werte zureferenzieren.

project.build.*

Sollten Sie sich auf Ausgabeverzeichnisse berufen müssen, so vermeiden Sieden Einsatz von direkten Bezügen zu den Verzeichnissen wie z.B.target/classes. Statt dessen sollten Sie Property Referenzen auf dieVerzeichnisse setzen.

• project.build.sourceDirectory

• project.build.scriptSourceDirectory

• project.build.testSourceDirectory

• project.build.outputDirectory

• project.build.testOutputDirectory

• project.build.directory

sourceDirectory, scriptSourceDirectory, und testSourceDirectory gebenIhnen Zugriff auf die Quellverzeichnisse des Projektes. outputDirectory sowietestOutputDirectory geben Ihnen Zugriff auf die Verzeichnisse, welcheMaven nutzt um den Byte Code oder Build Output abzulegen. directorybezieht sich auf das Verzeichnis, das alle derartigen Ausgabeverzeichnisseumfasst.

Weitere Projekt Property ReferenzesDarüber hinaus gibt es hunderte Properties welche man innerhalb eines POMreferenzieren kann. Eine vollständige Referenz des POM finden Sie unterhttp://maven.apache.org/ref/2.0.9/maven-model/maven.html.

Sollten Sie eine vollständige Dokumentation der Maven Modell Objekt Properties

Properties and Ressource Filterung

341

Page 361: Maven Definitive Guide De

benötigen, so bitte wir Sie auf die JavaDoc des Projektes maven-modelzurückzugreifen, diese finden Sie unter:http://maven.apache.org/ref/2.0.9/maven-model/apidocs/index.html. Nach demLaden der JavaDoc, sehen Sie sich die Klasse Model an. Von dieser Klasse aussollten Sie alle von Ihnen gesuchten POM Properties ansteuern können. Sollten Siedas Ausgabeverzeichnis des Builds referenzieren wollen, so sehen Sie in derMaven Model JavaDoc, dass dieses mittelsmodel.getBuild().getOutputDirectory(); erfolgt. Diesen Methodenaufrufwürden Sie in die Maven Property Referenz:${project.build.outputDirectory} wiederfinden.

Bezüglich weiterer Informationen zum Maven Model Module, dem Modul,welches die Struktur des POM definiert, möchten wir auf die Maven Model Seiteunter http://maven.apache.org/ref/2.0.9/maven-model verweisen.

13.2.2. Properties der Maven Einstellungen(settings.xml)Sie können auf jedes Property der lokalen Einstellungsdatei zugreifen, welchenormalerweise unter ~/.m2/settings.xml abgelegt ist. Diese Datei enthältbenutzerspezifische Einstellungen bezüglich der Maven Installation wie z.B. demOrt des lokalen Repository, der lokalen Server Infrastruktur, Profilen sowieSpiegel-Repositorien wie diese von einem Benutzer definiert wurden.

Eine ausführliche Referenz der lokalen Einstellungen sowie der entsprechendenProperties finden Sie unterhttp://maven.apache.org/ref/2.0.9/maven-settings/settings.html.

13.2.3. Properties der UmgebungsvariablenUmgebungsvariablen werden mit einem vorangestellten env.* eingeleitet. Einigeder interessanteren Umgebungsvariablen sind in der folgenden Liste aufgeführt:

env.PATH

Enthält den aktuellen Pfad (PATH)in welchem Maven läuft. PATH enthält eine

Properties and Ressource Filterung

342

Page 362: Maven Definitive Guide De

Liste von Verzeichnissen, in welchen ausführbare Skripte und Programmegesucht werden sollen.

env.HOME

(Unter *nix Systemen) zeigt diese Variable auf das “Heimatverzeichnis” desBenutzers. Besser und portabler ist es statt dessen ${user.home} einzusetzen.

env.JAVA_HOME

Enthält das Java Installationsverzeichnis. Dieses wird entweder auf einen JavaDevelopment Kit (JDK) oder auf eine Java Laufzeitumgebung (JRE) zeigen. Esempfiehlt sich statt dessen ${java.home} zu benutzen.

env.M2_HOME

Beinhaltet das Maven 2 InstallationsverzeichnisSolange die Java System Einstellungen verfügbar sind, sollten Sie diese einsetzen.Um auf das Heimatverzeichnis eines Benutzers zuzugreifen benutzen Sie${user.home} statt ${env.HOME}. Sie erzeugen damit einen portablerern Buildwelcher dem Versprechen von Write-One-Run-Anywhere (WORA) der JavaPlattform näherkommt.

13.2.4. Java System PropertiesMaven legt alle Properties von java.lang.System offen. Alle Daten, welche Siemittels System.getProperty() beziehen können, sind auch für Maven angreifbar.Die nachfolgende Tabelle führt die verfügbaren Properties auf:

Table 13.1. Java System Properties

System Property Beschreibung

java.version Java Laufzeitumgebung Version

java.vendor Java Laufzeitumgebung Hersteller

java.vendor.url Java Hersteller URL

java.home Java Installationsverzeichnis

Properties and Ressource Filterung

343

Page 363: Maven Definitive Guide De

System Property Beschreibung

java.vm.specification.version Java Virtual Machine SpezifikationVersion

java.vm.specification.vendor Java Virtual Machine SpezifikationHersteller

java.vm.specification.name Java Virtual Machine Spezifikation Name

java.vm.version Java Virtual Machine ImplementierungVersion

java.vm.vendor Java Virtual Machine ImplementierungHersteller

java.vm.name Java Virtual Machine ImplementierungName

java.specification.version Java Laufzeitumgebung SpezifikationVersion

java.specification.vendor Java Laufzeitumgebung SpezifikationHersteller

java.specification.name Java Laufzeitumgebung SpezifikationName

java.class.version Java Klassenformat Versionsnummber

java.class.path Java Klassenpfad

java.ext.dirs Pfad zum Verzeichnis der Erweiterungen

os.name Betriebssystem Name

os.arch Betriebssystem System Architekture

os.version Betriebssystem System Version

file.separator Datei Trennzeichen ("/" auf UNIX, "\" aufWindows)

Properties and Ressource Filterung

344

Page 364: Maven Definitive Guide De

System Property Beschreibung

path.separator Pfad Trennzeichen (":" auf UNIX, ";" aufWindows)

line.separator Zeilenumbruch ("\n" auf UNIX undWindows)

user.name Benutzerkonto Name

user.home Benutzerverzeichnis (home)

user.dir Aktuelles Arbeitsverzeichnis (desBenutzers)

13.2.5. Benuzerdefinierte PropertiesZusätzlich zu den implizit von POM, den Maven Einstellungen,Umgebungsvariablen und Java System Einstellungen bereitgestellten Propertieshaben Sie die Möglichkeit Ihre eigenen beliebigen Properties zu setzen. Propertieskönnen als Teil eines POM oder Profil definiert werden. Die Menge der Propertieseines POMs oder Maven Profils kann wie alle anderen Properties in Mavenangesprochen werden. Benutzerdefinierte Properties können innerhalb einer POMDatei referenziert werden, oder Sie können eingesetzt werden um Ressourcenmittels dem Maven Resource Plugin zu filtern. Hier ein Beispiel wie man einbeliebiges Property innnerhalb einer POM Datei definiert:

Example 13.1. Definition eines benutzerdefinierten Property im POM

<project>...<properties>

<arbitrary.property.a>This is some text</arbitrary.property.a><hibernate.version>3.3.0.ga</hibernate.version>

</properties>...<dependencies>

<dependency><groupId>org.hibernate</groupId><artifactId>hibernate</artifactId>

Properties and Ressource Filterung

345

Page 365: Maven Definitive Guide De

<version>${hibernate.version}</version></dependency>

</dependencies>...

</project>

Das vorgehende Beispiel definiert zwei Properties: arbitrary.property.a undhibernate.version. hibernate.version wird innerhalb einerAbhängigkeitsdefinition eingesetzt. Der Einsatz des Punkt-Zeichens (.) alsTrennzeichen in Propertynamen hat sich als gängige Schreibweise für Propertiesdurchgesetzt; Für Maven ist „hibernate.version“ lediglich ein Schlüssel um denWert „3.3.0.ga“ aufzufinden.

Example 13.2. Definition eines benutzerdefinierten Property innerhalb einerProfildefinition im POM

<project>...<profiles>

<profile><id>some-profile</id><properties>

<arbitrary.property>This is some text</arbitrary.property></properties>

</profile></profiles>...

</project>

Das vorangegangene Beispiel verdeutlicht wie man ein benutzerdefiniertesProperty innerhalb einer Profildefinition eines Maven POM definiert. WeitereDetails und Informationen bezüglich benutzerdefinierter Properties und Profilenfinden sie im Chapter 11, Build Profiles (Kapitel 11: Build Profile).

13.3. Ressource FilterungSie können Maven auch dazu einsetzen, Variablen innerhalb von Projektressourcenzu ersetzen. Sobald Ressourcen Filterung aktiviert ist, durchsucht Maven

Properties and Ressource Filterung

346

Page 366: Maven Definitive Guide De

Ressourcen nach Property Referenzen eingeschlossen in ${ und}. Sobald es einederartige Referenz gefunden hat, ersetzt es diese mit dem entsprechenden Wertganz ähnlich wie dies auch mit den Property Referenzen eines POM geschieht undim letzen Abschnitt beschrieben wurde. Diese Möglichkeit ist insbesondere dannhilfreich, wenn es notwendig wird einen Build zu parametrisieren um verschiedeKonfigurationen zu übergeben, abhängig von der entsprechenden Zielplattform.

Oftmals wird eine Datei .properties oder ein XML Dokument unter/src/main/resources die Definition externer Ressource wieDatenbankverbindungen oder Netzwerkparameter enthalten, welche abhängig vonder Zielplatform unterschiedlich konfiguriert werden müssen. So hat z.B. einSystem welches von einer Datenbank lesen muss eine Datei welche die JDBCURL zusammen mit den entsprechenden Parametern für den Datenbankzugriffangibt. Benutzen Sie eine andere Datenbank während der Entwicklung als in derProduktion (was zu erwarten ist), so können Sie entweder dieseRessourcedefinition mittels einer Technologie wie z.B. JNDI externalisieren, oderaber Sie generieren einen Build welcher es versteht, die Zielplattform abhängigenWerte mit den entsprechenden Werten zu setzen.

Unter Einsatz von Maven Ressource Filterung können Sie Maven Properties setzenund diese dann mittels Maven Profilen entsprechend der benötigten Zielplattformdefinieren. Um diese Möglichkeit zu illustrieren, hier ein Beispiel: gehen Siedavon aus, dass Ihr Projekt auf dem Spring Framework beruht, welches eineBasicDataSource aus dem CommonsDBCP Projekt konfiguriert. Ihr Projekt wird alsounter src/main/resources eine Datei mit dem Namen applicationContext.xml

ablegen, welche die in Example 13.3, “Maven Properties aus einer Ressource Dateireferenzieren” (Beispiel 13.3: Maven Properties aus einer Ressource Dateireferenzieren) ausgeführten Daten enthält.

Example 13.3. Maven Properties aus einer Ressource Datei referenzieren

<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-2.5.xsd">

<bean id="someDao" class="com.example.SomeDao"><property name="dataSource" ref="dataSource"/>

Properties and Ressource Filterung

347

Page 367: Maven Definitive Guide De

</bean>

<bean id="dataSource" destroy-method="close"class="org.apache.commons.dbcp.BasicDataSource">

<property name="driverClassName" value="${jdbc.driverClassName}"/><property name="url" value="${jdbc.url}"/><property name="username" value="${jdbc.username}"/><property name="password" value="${jdbc.password}"/>

</bean></beans>

Ihr Programm würde diese Datei zur Laufzeit lesen, und Ihr Build wird Referenzenauf Properties wie z.B. jdbc.url oder jdbc.username mit den Werten welche Siein Ihrer pom.xml-Datei setzen ersetzen. Ressource Filterung ist standardmässigausgeschaltet, um die unbewusste und ungewollte Filterung von Werten zuverhindern. Um den Ressource Filter einzustellen, müssen Sie das abgeleiteteElement resource innerhalb ihrer POM Datei setzen. Example 13.4, “Definierenvon Variablen und Aktivierung von Ressource Filterung” (Beispiel 13.4:Definieren von Variablen und Aktivierung von Ressource Filterung) illustriert einPOM welches die Variablen aus Example 13.3, “Maven Properties aus einerRessource Datei referenzieren” (Beispiel 13.3: Maven Properties aus einerRessource Datei referenzieren) definiert und Ressource Filterung für alleRessourcen unter src/main/resources aktiviert

Example 13.4. Definieren von Variablen und Aktivierung von RessourceFilterung

<project>...<properties>

<jdbc.driverClassName>com.mysql.jdbc.Driver</jdbc.driverClassName>

<jdbc.url>jdbc:mysql://localhost:3306/development_db</jdbc.url><jdbc.username>dev_user</jdbc.username><jdbc.password>s3cr3tw0rd</jdbc.password>

</properties>...<build>

<resources><resource>src/main/resources</resource><filtering>true</filtering>

</resources></build>

Properties and Ressource Filterung

348

Page 368: Maven Definitive Guide De

...<profiles>

<profile><id>production</id><properties>

<jdbc.driverClassName>oracle.jdbc.driver.OracleDriver</jdbc.driverClassName><jdbc.url>jdbc:oracle:thin:@proddb01:1521:PROD</jdbc.url><jdbc.username>prod_user</jdbc.username><jdbc.password>s00p3rs3cr3t</jdbc.password>

</properties></profile>

</profiles></project>

Die vier entsprechenden Variablen werden jeweils innerhalb des Elementsproperties definiert und Ressource Filterung wird für Ressourcen untersrc/main/ressources gesetzt. Da Ressource Filterung standardmässig deaktiviertist, ist es notwendig für Ihr Projekt explizit den Wert des Elements filtering auftrue zu setzen. Wenn Sie nun ein Projekt mit der Ressource aus Example 13.3,“Maven Properties aus einer Ressource Datei referenzieren” (Beispiel 13.3: MavenProperties aus einer Ressource Datei referenzieren) und den POM Einträgen ausExample 13.4, “Definieren von Variablen und Aktivierung von RessourceFilterung” (Beispiel 13.4: Definieren von Variablen und Aktivierung vonRessource Filterung) builden und Sie dann den Inhalt der Ressource untertarget/classes ansehen, so sollten Sie dort die folgende, gefilterte Ressourcewiederfinden:

$ mvn install...$ cat target/classes/applicationContext.xml...

<bean id="dataSource" destroy-method="close"class="org.apache.commons.dbcp.BasicDataSource">

<property name="driverClassName" value="com.mysql.jdbc.Driver"/><property name="url" value="jdbc:mysql://localhost:3306/development_db"/><property name="username" value="dev_user"/><property name="password" value="s3cr3tw0rd"/>

</bean>...

Das POM aus Example 13.4, “Definieren von Variablen und Aktivierung vonRessource Filterung” (Beispiel 13.4: Definieren von Variablen und Aktivierungvon Ressource Filterung) definiert ebenfalls unter dem Element profiles/profile

Properties and Ressource Filterung

349

Page 369: Maven Definitive Guide De

ein Profile production, welches das Standardprofil mit Werten welche für eineProduktionsumgebung sinnvoll sind, übersteuert. In diesem ausgeführten Beispieldefiniert das POM Standardwerte für die Datenbankverbindung auf eine lokalinstallierte MySQL™ Instanz. Sollte der Build mit aktiviertem Profil productionerstellt werden, so wird Maven das System derart konfigurieren, dass fortan dasSystem gegen eine Oracle Datenbank mit anderen Treibern, URL, Benutzernameund Passwort zum Zuge kommt. Wenn sie also den Build mit den obenausgeführten Beispielen und aktivierten Profil production ausgeführt haben, sosollten Sie unter target/classes eine Ressource mit den unten aufgeführtenproduktiven Werten auffinden.

$ mvn -Pproduction install...$ cat target/classes/applicationContext.xml...

<bean id="dataSource" destroy-method="close"class="org.apache.commons.dbcp.BasicDataSource">

<property name="driverClassName"value="oracle.jdbc.driver.OracleDriver"/>

<property name="url" value="jdbc:oracle:thin:@proddb01:1521:PROD"/><property name="username" value="prod_user"/><property name="password" value="s00p3rs3cr3t"/>

</bean>...

Properties and Ressource Filterung

350

Page 370: Maven Definitive Guide De

Chapter 14. Maven in Eclipse: m2eclipse

14.1. EinführungDie Eclipse IDE ist heutzutage wohl die Entwicklungsumgebung mit der grösstenVerbreitung. Es gibt eine riesige Anzahl verfügbarer Plugins(http://www.eclipseplugincentral.com/) und eine unzählbar grosse AnzahlOrganisationen welche deren Software auf Eclipse entwickeln. Einfachausgedrückt: Eclipse ist allgegenwärtig. Das m2eclipse Projekt bietet eine MavenUnterstützung von innerhalb der Entwicklungsumgebung. In diesem Kapitelwerden wir die Möglichkeiten welche uns dieses Werkzeug bietet erkunden.

14.2. m2eclipseDas m2eclipse Plugin (http://m2eclipse.codehaus.org/) bietet eineMavenintegration für Eclipse. M2eclipse bietet ebenfalls Möglichkeiten dieFunktionen von zwei weiteren Plugins, dem Subclipse Plugin(http://subclipse.tigris.org) sowie dem Mylin Plugin(http://www.eclipse.org/mylyn) zu nutzen. Das Subclipse Plugin bietet demm2eclipse Plugin die Möglichkeit direkt mit einem Subversion Repositorien zuinteragieren und das Mylyn Plugin erweitert das m2eclipse Plugin um mit demweit verbreiteten Mylyn Interface zu zu interagieren. Dieses hilft die Übersicht imEntwicklungszusammenhang zu behalten. Hier eine Reihe Funktionalitäten welchem2eclipse bietet:

• Erstellen sowie importieren von Maven Projekten

• Abhängigkeitsverwaltung und Integration mit dem Eclipse Klassenpfad

• Automatischer Download von Abhängigkeiten und deren Update

• Artefakt JavaDoc sowie Quellen Auflösung

351

Page 371: Maven Definitive Guide De

• Erstellen von Projekten basierend auf Maven Artefakten

• Durchsuchen und Suchen in entfernten Maven Repositorien

• POM Verwaltung mit automatischem Update der Abhängigkeitsliste

• Materialisierung ausgehend von einem Maven POM

• Auschecken eines Maven Projektes von mehreren verteilten SCMRepositorien

• Anpassen von verschachtelten multi-modularen Maven Projekten auf dieEntwicklungsumgebung Eclipse

• Integration mit dem Web Tools Projekt (WTP)

• Integration mit AspectJ Development Tools (AJDT)

• Integration mit Subclipse

• Integration mit Mylyn

• Formularbasierter POM Editor

• Graphische Darstellung des Abhängigkeitsgraphen

• GUI Darstellung des Abhängigkeitsbaumes und der aufgelöstenAbhängigkeiten

Es gibt über diese Liste hinaus gibt es noch viele weitere Funktionalitäten welchem2eclipse bietet. Dieses Kapitel wird Sie in einige der eindrucksvollerenFähigkeiten einführen, welche derzeit verfügbar sind. Lassen Sie uns damitbeginnen, das m2eclipse Plugin zu installieren.

14.3. Installation des m2eclipse Plugins

Maven in Eclipse: m2eclipse

352

Page 372: Maven Definitive Guide De

Um das m2eclipse Plugin lauffähig zu installieren, müssen etlicheVoraussetzungen erfüllt sein. Sie müssen mindestens auf Eclipse 3.2 aufsetzen,mindestens JDK 1.4 benutzen und sicherstellen, dass Sie auf einem JDK und nichtnur einer Java Runtime (JRE) basieren. Sobald Sie Eclipse sowie einenentsprechenden JDK am Laufen haben, müssen Sie zunächst noch zwei Pluginsinstallieren: Subclipse und Mylyn.

14.3.1. Installieren der VoraussetzungenSie können die Voraussetzungen zusammen mit m2eclipse installieren: fügen Sieeinfach die den Komponennten entsprechenden Update Sites in Eclipse an. Umdiese Komponenten dann zu installieren, wählen Sie Hilfe >Software Updates>Suchen und Installieren... Es wird die Update-/Installationsschaltfläche anzeigen.Wählen Sie „nach neuen zu installierenden Features suchen“ und wählen Sei„weiter“. Die Anzeige wird auf „xxx“ wechseln. Wählen Sie „Neuer Remote Site“und fügen Sie für jede Komponente die entsprechende Site hinzu, stellen Sie sicherdass der entsprechende Eintrag ausgewählt ist. Zum Abschluss wird Eclipse ihnenanbieten die ausgewählten Komponenten zu installieren. Eclipse wird diese dannherunterladen, installieren und konfigurieren.

Sollten Sie die neuere Version Eclipse 3.4 (Ganymede) benutzen, so wird diePlugin-Installation leicht von dieser Beschreibung abweichen: unter Ganymedewählen Sie Hilfe > Software Updates und öffnen hiermit den Dialog: „xxx“.Innerhalb dieses Fensters wählen Sie die „Verfügbare Software“ und dann „Sitehinzufügen“ welches den Dialog „Site hinzufügen" öffnen wird. Geben Sie dieURL des Sites welchen Sie hinzufügen möchten ein und bestätigen mit "OK". ImDialogfenster „Software Updates and Add-Ons" werden die entsprechendverfügbaren Plugins der hinzugefügten Sites erscheinen. Sie können nun dieentsprechenden Module auswählen und installieren. Eclipse wird dieentsprechenden Abhängigkeiten auflösen und gegebenenfalls darum bitten dieSoftware Lizenzbedingungen zu akzeptieren. Nach der Installation wird EclipseSie wahrscheinlich aufrufen Eclipse einem Neustart zu unterziehen.

14.3.1.1. Installation von Subclipse

Maven in Eclipse: m2eclipse

353

Page 373: Maven Definitive Guide De

Um Subclipse zu installieren, benutzen Sie den unten aufgeführten Eclipse PluginUpdate Site:

• Subclipse 1.2: http://subclipse.tigris.org/update_1.2.x

Um andere Versionen von Subclipse zu installieren, oder um weitereInformationen bezüglich des Subclipse Plugin zu erlagen, wenden Sie sich and denSubclipse Projekt Website: http://subclipse.tigris.org/.

14.3.1.2. Installation von MylynTo install JIRA integration with Mylyn, add the Mylyn extras Eclipse update URL,you'll want to do this if your organization uses Atlassian's JIRA for issue tracking.To install Mylyn use the following update sites:

Um die JIRA Integration mittels Mylyn zu installieren, fügen Sie die Update Siteder Eclipse Mylyn Extras hinzu. Dies werden Sie insbesondere tun wollen, wennIhre Organisation Atlassian JIRA Issue Tracking einsetzt. Um Mylyn zuinstallieren, benutzen Sie einen der folgenden Update Sites:

• Mylyn (Eclipse 3.3): http://download.eclipse.org/tools/mylyn/update/e3.3

• Mylyn (Eclipse 3.4): http://download.eclipse.org/tools/mylyn/update/e3.4

• Mylyn Extras (JIRA Support):http://download.eclipse.org/tools/mylyn/update/extras

Um weitere Informationen bezüglich des Mylyn Plugin zu erlagen, wenden Siesich and den Mylin Projekt Website: http://www.eclipse.org/mylyn/.

14.3.1.3. Installation der AspectJ Development Tools (AJDT)Sollten Sie die Version 0.9.4 von m2eclipse installieren, so werden Sie eventuellauch die beiden Erweiterungen: Web Tools Platform (WTP) sowie AspectJDevelopment Tools (AJDT) installieren wollen. Um AJDT zu installieren,benutzen Sie:

Maven in Eclipse: m2eclipse

354

Page 374: Maven Definitive Guide De

• AJDT (Eclipse 3.3): http://download.eclipse.org/tools/ajdt/33/update

• AJDT (Eclipse 3.4): http://download.eclipse.org/tools/ajdt/34/dev/update

Um weitere Informationen bezüglich des AspectJ Development Tools zu erlagen,wenden Sie sich and den AJDT Projekt Website: http://www.eclipse.org/ajdt/.

14.3.1.4. Installation der Web Tools Platform (WTP)To install the Web Tools Platform (WTP). Use one of the following update URLsin Eclipse, or just look for the Web Tools Project in the Discovery Site whichshould already be in your Eclipse remote update sites list.

Um die Web Tools Platform (WTP) zu installieren, benutzen Sie die untenstehende Update URL in Eclipse oder suchen Sie das Web Tools Project innerhalbdes Discovery Site welcher bereits Teil der Eclipse Remote Update Site Liste seinsollte.

• WTP: http://download.eclipse.org/webtools/updates/

Um weitere Informationen bezüglich der Web Tools Platform zu erlagen, wendenSie sich and den WTP Projekt Website http://www.eclipse.org/webtools/.

14.3.2. Installation von m2eclipseSobald Sie diese Voraussetzungen erfüllt haben und die oben aufgeführtenKomponenten installiert haben, können Sie mit der Installation von m2Eclipsefortfahren. Benutzen Sie hierzu die folgende Update URL:

• m2eclipse Plugin: http://m2eclipse.sonatype.org/update/Sollten Sie den neusten Entwickler Snapshot Release des Plugins bevorzugen,wählen Sie stattdessen die Update Development URL:

• m2eclipse Plugin (Development Snapshot):http://m2eclipse.sonatype.org/update-dev/

Maven in Eclipse: m2eclipse

355

Page 375: Maven Definitive Guide De

Um das m2eclipse Plugin zu installieren, wählen Sie Hilfe > Software Update >Suchen und Installieren … Es wird die Update-/Installationsschaltfläche angezeigt.Wählen Sie „nach neuen zu installierenden Features suchen“ und wählen Sei„weiter“. Die Anzeige wird auf „xxx“ wechseln. Wählen Sie „Neuer Remote Site“und fügen Sie die oben angegebene Site hinzu. Stellen Sie sicher, dass derentsprechende Eintrag ausgewählt ist. Zum Abschluss wird Eclipse ihnen anbietenm2eclipse zu installieren. Eclipse wird die Komponente dann herunterladen,installieren und konfigurieren.

Mit der erfolgreichen Installation des Plugins sollten Sie unter Windows >Preferences in der Liste der Optionen einen Eintrag "Maven" finden.

14.4. Aufschalten der Maven KonsoleVor wir nun tatsächlich anfangen die Möglichkeiten des m2eclipse Pluginsauszuleuchten, lassen Sie uns zunächst die Maven Konsole aufschalten. Öffnen Siedie Konsolenansicht (Windows > Ansichten > Konsole). Wählen Sie den kleinenPfeil rechts vom Symbol Konsole öffnen und dort "Maven Konsole"; wie untendargestellt:

Figure 14.1. Aufschalten der Maven Konsole in Eclipse

Die Maven Konsole gibt alle Ausgaben wieder, welche normalerweise auf demKonsolenfenster ausgegeben werden, wenn Maven von der Befehlszeile gestartetwird. Dies ist sehr nützlich um zu sehen was Maven gerade macht, oder umDebugausgaben zu analysieren und Probleme anzusehen.

Maven in Eclipse: m2eclipse

356

Page 376: Maven Definitive Guide De

14.5. Erstellen eines Maven ProjektsBeim Einsatz von Maven werden Projekte gewöhlich durch den Einsatz vonMaven Archetypen erstellt. Innerhalb von Eclipse werden neue Projekte durch den„Neue Projekte Wizard“ erstellt. Der Eclipse "Neues Projekt"-Wizard bietet eineFülle verschiedener Vorlagen um neue Projekte zu erstellen! Das m2eclipse Pluginbaut auf diesen Wizard auf, und bietet die folgenden zusätzlichen Möglichkeiten:

• Auschecken eines Maven Projekts aus einem SCM Repository

• Erstellen eines Maven Projekts auf der Basis eines Maven Archetyps

• Erstellen einer Maven POM Datei

Wie in Figure 14.2, “Erstellen eines Neuen Projekts mit Hilfe des m2eclipseWizards” (Abbildung 14.2: "Erstellen eines Neuen Projekts mit Hilfe desm2eclipse Wizards") dargestellt, sind alle drei Möglichkeiten für einen Entwicklervon Interesse. Lassen Sie uns diese Möglichkeiten im Einzelnen genauer ansehen.

Maven in Eclipse: m2eclipse

357

Page 377: Maven Definitive Guide De

Figure 14.2. Erstellen eines Neuen Projekts mit Hilfe des m2eclipse Wizards

14.5.1. Auschecken eines Maven Projektes von einemSCM Repositorym2eclipse bietet die Möglichkeit Projekte direkt aus einem SCM Repositoryauszuchecken. Geben Sie einfach die SCM Daten des Projektes ein und m2eclipsewird das Projekt an den von Ihnen angegebenen Ort auschecken. Dies ist inFigure 14.3, “Auschecken eines Projekts aus Subversion” (Abbildung 14.3:

Maven in Eclipse: m2eclipse

358

Page 378: Maven Definitive Guide De

"Auschecken eines Projekts aus Subversion" beispielhaft dargestellt:

Figure 14.3. Auschecken eines Projekts aus Subversion

Es bestehen zusätzlichen Optionen als Teil dieses Dialogs. Diese ErmöglichenIhnen eine bestimmte Version auszuwählen, entweder indem Sie im Katalog desSubversion Repository auswählen, oder einfach durch die manuelle Eingabe

Maven in Eclipse: m2eclipse

359

Page 379: Maven Definitive Guide De

Revisionsnummer. Diese Funktionalitäten bauen auf Funktionen des SubclipsePlugins auf, um mit dem Subclipse Repository zu interagieren. Darüber hinausunterstützt das m2eclipse Plugin die folgenden SCM Anbieter:

• Bazaar

• Clearcase

• CVS

• git

• hg

• Perforce

• Starteam

• Subversion

• Synergy

• Visual SourceSafe

14.5.2. Erstellen eines Maven Projekts auf der Basiseines Maven Archetypsm2eclipse ermöglicht ein Maven Projekt auf der Basis eines Maven Archetyps zuerstellen. Es bestehen sehr viele verschiedene Maven Archetypen, diese werdenbereits in der Auswahlliste des m2eclipse aufgeführt werden, wie aus derFigure 14.4, “Erstellen eines Neuen Projektes auf der Basis eines MavenArchetyps” (Abbildung 14.4: "Erstellen eines Neuen Projektes auf der Basis einesMaven Archetyps" ersichtlich wird:

Maven in Eclipse: m2eclipse

360

Page 380: Maven Definitive Guide De

Figure 14.4. Erstellen eines Neuen Projektes auf der Basis eines MavenArchetyps

Die Liste der aufgeführten Muster aus Figure 14.4, “Erstellen eines NeuenProjektes auf der Basis eines Maven Archetyps” (Abbildung 14.4: "Erstellen einesNeuen Projektes auf der Basis eines Maven Archetyps") ist eine Aufzählungwelche von einem Werkzeug genannt Nexus Indexer erzeugt wurde. Nexus ist einRepository Manager auf welchen in Chapter 16, Repository Management withNexus (Kapitel 16: "Repository Manager") näher eingegangen wird. Der NexusIndexer ist eine Datei die einen Index über das gesamte betrachtete Maven

Maven in Eclipse: m2eclipse

361

Page 381: Maven Definitive Guide De

Repository darstellt. m2eclipse benutzt diese um alle bereitstehenden Archetypendes gesamten Maven Repository darzustellen. Als dieses Kapitel das letzte Malangepasst wurde, wurden bereits ca. 90 verschiedene Muster im Archetypdialogdargestellt. Darunter besonders erwähnenswert sind:

• Die Standardarchetypen von Maven zur Erstellung von

• Maven Plugins

• Einfache Web Anwendungen

• Einfache Projekte

• Neue Maven Archetypen

• Databinder Archetypen (datengetriebene Wicket Anwendungen) unternet.databinder

• Apache Cocoon Archetypen unter org.apache.cocoon

• Apache Directory Server Archetypen unter org.apache.directory.server

• Apache Geronimo Archetypen unter org.apache.geronimo.buildsupport

• Apache MyFaces Archetypen unter org.apache.myfaces.buildtools

• Apache Tapestry Archetypen unter org.apache.tapestry

• Apache Wicket Archetypen unter org.apache.wicket

• AppFuse Archetypen unter org.appfuse.archetypes

• Codehaus Cargo Archetypen unter org.codehaus.cargo

• Codehaus Castor Archetypen unter org.codehaus.castor

• Groovy-based Maven Plugin Archetypen (deprecated)24 unter

Maven in Eclipse: m2eclipse

362

Page 382: Maven Definitive Guide De

24[4] Verzichten Sie auf den Einsatz des Groovy Maven Plugin des Codehaus Mojo Projekt. JasonDillon hat inzwischen die Groovy Maven Integrationsbemühungen in das Groovy Projekt von Codehausverschoben. Für weitere Informationen wenden Sie sich an http://groovy.codehaus.org/GMaven.

org.codehaus.mojo.groovy

• Jini Archetypen

• Mule Archetypen unter org.mule.tools

• Objectweb Fractal Archetypen unter org.objectweb.fractal

• Objectweb Petals Archetypen unter org.objectweb.petals

• ops4j Archetypen unter org.ops4j

• Parancoe unter org.parancoe

• slf4j Archetypen unter org.slf4j

• Springframework OSGI sowie Web Services Archetypen unterorg.springframework

• Trails Framework Archetypen unter org.trailsframework24And these were just the archetypes that were listed under the Nexus IndexerCatalog, if you switch Catalogs you'll see other archetypes. While your results mayvary, the following additional archetypes were available in the Internal Catalog:

Und dies sind nur die Archetypen welche vom Nexus Indexer Katalog afugeführtwerden, sollten Sie einen anderen Katalog auswählen, so werden Sie weitereArchetypen kennen lernen. Die Resultate werden unterschiedlich von Fall zu Fallunterschiedlich sein, im internen Katalog waren zum Beispiel folgendezusätzlichen Muster verfügbar:

• Atlassian Confluence Plugin Archetype untercom.atlassian.maven.archetypes

Maven in Eclipse: m2eclipse

363

Page 383: Maven Definitive Guide De

• Apache Struts Archetypen unter org.apache.struts

• Apache Shale Archetypen unter org.apache.shale

Ein Katalog ist einfach eine Referenz auf einen Repository Index. Sie können dieMenge aller Kataloge welche vom m2eclipse Plugin berücksichtigt werdenverwalten, indem Sie neben der Drop Down Box des Kataloges den KnopfConfigure… auswählen. Sollten Sie Ihren eigenen Archetyp einem Katalogzufügen wollen, so wählen Sie Add Archetype …

Sobald Sie einen Archetypen ausgewählt haben, wird Maven den entsprechendenArtefakten aus dem Maven Repository holen und ein entsprechendes EclipseProjekt erstellen.

14.5.3. Erstellen eines Maven Modulsm2eclipse bietet auch die Möglichkeit ein Maven Modul zu erstellen. Dies istbeinahe identisch zur Erstellung eines Maven Projektes, da es ebenfalls ein MavenProjekt auf der Basis eines Archetypen erstellt. Jedoch ist ein Maven Modul einUnterprojekt zu einem anderen Maven Projekt, das gemeinhin als übergeordnetesoder elterliches Projekt bezeichnet wird.

Maven in Eclipse: m2eclipse

364

Page 384: Maven Definitive Guide De

Figure 14.5. Erstellen eines neuen Maven Modules

Beim Erstellen eines neuen Maven Moduls müssen Sie ein übergeordnetes Projektauswählen, welches bereits innerhalb der Entwicklungsumgebung existiert. DieAuswahl des Buttons Browse zeigt eine Liste der bereits bestehenden Projekte wiein Figure 14.6, “Auswahl eines übergeordneten Projektes eines neues MavenModul” (Abbildung 14.6: "Auswahl eines übergeordneten Projektes eines neues

Maven in Eclipse: m2eclipse

365

Page 385: Maven Definitive Guide De

Maven Modul") dargestellt.

Figure 14.6. Auswahl eines übergeordneten Projektes eines neues Maven

Maven in Eclipse: m2eclipse

366

Page 386: Maven Definitive Guide De

Modul

Nach der Auswahl eines übergeordneten Projektes aus der Liste werden Sie auf das"Neues Maven Modul"-Fenster zurückgeleitet, dort ist nun das FeldÜbergeordnetes Projekt ausgefüllt wie in Figure 14.5, “Erstellen eines neuenMaven Modules” (Abbildung 14.5: "Erstellen eines neuen Maven Moduls")dargestellt. Mit der Auswahl von Weiter werden Sie zur Liste der MavenArchetypen wie in Section 14.5.2, “Erstellen eines Maven Projekts auf der Basiseines Maven Archetyps” (14.5.2: "Erstellen eines Maven Projekts auf der Basiseines Maven Archetyp") ausgeführt. Sie können dabei auswählen auf der Basiswelchen Musters das neue Maven Modul erstellt werden soll.

14.6. Erstellen einer Maven POM DateiEine weitere wichtige Funktionalität des m2eclipse-Plugins ist es POM-Dateien zuerstellen. m2eclipse stellt einen Wizard bereit, um für ein bestehendes Projekt einePOM Datei zu erstellen. Dieser POM Wizard wird in Figure 14.7, “Abbildung14.7: Erstellen eines neuen POMs” (Abbildung 14.7: Erstellen eines neuen POMs)dargestellt:

Maven in Eclipse: m2eclipse

367

Page 387: Maven Definitive Guide De

Figure 14.7. Abbildung 14.7: Erstellen eines neuen POMs

Zu einem bestehenden Projekt ein neues Maven POM zu erstellen ist einfach:Auswählen des Projektes, Eingabe der groupID, artefactID sowie version,Auswahl des Paketierungstyps und Vergabe eines Namens in den dafürvorgesehenen Feldern des m2eclipse Wizards. Wählen Sie weiter umAbhängigkeiten zuzufügen.

Maven in Eclipse: m2eclipse

368

Page 388: Maven Definitive Guide De

Figure 14.8. Hinzufügen von Abhängigkeiten zu einer neuen POM Datei

Wie Sie leicht aus Figure 14.8, “Hinzufügen von Abhängigkeiten zu einer neuenPOM Datei” (Abbildung 14.8: "Hinzufügen von Abhängigkeiten zu einer neuenPOM Datei") ersehen, sind zunächst keine Abhängigkeiten enthalten. Wählen Siezufügen um das zentrale Maven Repository nach Abhängigkeiten zu durchsuchen.Dies ist in der nächsten Abbildung, Figure 14.9, “Durchsuchen des ZentralenRepositories nach Abhängigkeiten” (Abbildung 14.9: "Durchsuchen des Zentralen

Maven in Eclipse: m2eclipse

369

Page 389: Maven Definitive Guide De

Repositories nach Abhängigkeiten") dargestellt:

Figure 14.9. Durchsuchen des Zentralen Repositories nach Abhängigkeiten

Nach Abhängigkeiten zu suchen ist einfach; geben Sie die groupId des benötigtenArtefakten ein. Figure 14.9, “Durchsuchen des Zentralen Repositories nach

Maven in Eclipse: m2eclipse

370

Page 390: Maven Definitive Guide De

Abhängigkeiten” (Abbildung 14.9: "Durchsuchen des Zentralen Repositories nachAbhängigkeiten") stellt die Abfrage nach org.apache.commons dar, wobeicommons-vfs geöffnet ist, um zu sehen welche Versionen verfügbar sind. DieAuswahl der 1.1-SNAPSHOT Version von commons-vfs und nachfolgende Auswahlvon Ok führt Sie zurück in den Dialog zur Auswahl der Abhängigkeiten, wo Siedann weitere Abhängigkeiten hinzufügen können, oder via Abschliessen das POMerstellen können. Bei der Suche nach Abhängigkeiten macht m2eclipse Gebrauchvon dem gleichen Nexus Repository Index, welcher auch von Nexus IndexManager aus Chapter 16, Repository Management with Nexus (Kapitel 16:"Repository Manager") benutzt wird.

Jetzt, da Sie die Funktionalität welche m2eclipse zur Erstellung eines neuenProjektes bietet gesehen haben, lassen Sie uns die verwandten Funktionen zumImport von Projekten ansehen:

14.7. Importieren von Maven Projektenm2eclipse bietet dreierlei Möglichkeiten Maven Projekte in Eclipse zu importieren:

• Importieren eines existierenden Maven Projekts

• Auschecken eines Maven Projektes aus einem SCM Repository

• Materialisieren eines Maven Projektes

Figure 14.10, “Importieren eines Maven Projektes” (Abbildung 14.10:"Importieren eines Maven Projektes") stellt den Wizard zum Import von Projektenmit den zugehörigen Optionen, welche Maven durch das m2eclipse Pluginbereitstellt, dar.

Maven in Eclipse: m2eclipse

371

Page 391: Maven Definitive Guide De

Figure 14.10. Importieren eines Maven Projektes

Die Dialogbox aus Figure 14.10, “Importieren eines Maven Projektes” (Abbildung14.10: "Importieren eines Maven Projektes") kann über Datei > Importieren undanschliessendem Filtern mit dem Schlüsselbegriff maven, innerhalb von Eclipseaufgerufen werden. Wie oben erwähnt bieten sich drei Möglichkeiten ein MavenProjekt zu in Eclipse zu importieren: Maven Projekte Importieren, Auscheckeneines Maven Projektes aus einem Repository und Materialisieren von MavenProjekten.

Maven in Eclipse: m2eclipse

372

Page 392: Maven Definitive Guide De

Importieren eines Maven Projektes aus einem SCM Repository ist identisch zurFunktionalität "Erstellen eines Maven Projektes aus einem SCM Repository"welche vorgängig Diskutiert wurde; eine weitere Behandlung ist überflüssig.Lassen Sie uns weitergehen zu den zwei verbleibenden Möglichkeiten ein MavenProjekt zu importieren.

14.7.1. Importiren eines Maven Projektesm2eclipse kann ein Projekt mit bestehender pom.xml-Datei importieren. DurchAngabe des Verzeichnisses in welchem das Maven Projekt abgelegt wurde, istm2eclipse in der Lage alle Maven POMs des Projekts zu finden und einehierarchische Liste dieser aufzustellen. Diese ist in Figure 14.11, “Importiereneines multi-modularen Maven Projektes ” (Abbildung 14.11: "Importieren einesmulti-modularen Maven Projektes") dargestellt:

Maven in Eclipse: m2eclipse

373

Page 393: Maven Definitive Guide De

Figure 14.11. Importieren eines multi-modularen Maven Projektes

Figure 14.11, “Importieren eines multi-modularen Maven Projektes ” (Abbildung14.11: "Importieren eines multi-modularen Maven Projektes") zeigt dieDarstellung des zu importierenden Projektes. Bitte beachten Sie, dass alle POMsdes Projektes in hierarchischer Darstellung wiedergegeben werden. Das erlaubtIhnen auf einfache Art und Weise genau auszuwählen, welche POMs (und damitProjekte) Sie in Eclipse importieren möchten. Haben Sie erst einmal die Projektewelche Sie importieren möchten ausgewählt, nimmt Ihnen m2eclipse den Import

Maven in Eclipse: m2eclipse

374

Page 394: Maven Definitive Guide De

und das Builden der Projekte mit der Unterstützung von Maven ab.

14.7.2. Materialisieren eines Maven ProjektesEine weitere Funktionalität welche m2eclipse bietet ist die Materialisierung einesProjektes. Materialisierung ist ähnlich dem Auschecken eines Projektes aus einemSCM Repository, jedoch, anstatt die URL des Repository manuell einzugeben,wird diese aus der obersten POM Datei des Projektes gelesen. Sie können dieseFunktionalität dazu benutzen, Projekte aus nichts weiter als einer pom.xml Datei,welche die entsprechenden Elemente enthält, zu erstellen. Mit dieser Möglichkeitist es Ihnen gegeben, das Maven Repository zu durchstreifen und die enthaltenenProjekte in Eclipse zu materialisieren. Dies ist insbesondere nützlich, sollte IhrProjekt von einer Open Source Library abhängig ist, und Sie versuchen an dessenQuellen zu gelangen. Statt mühsam den Projekt-Website herauszufinden undfestzustellen, wie Sie diese aus dem Repository auschecken können, benutzen Sieeinfach m2eclipse um das Projekt zu materialisieren.

Figure 14.12, “Materialisieren eines Maven Projektes” (Abbildung 14.12:"Materialisieren eines Maven Projektes") stellt den Wizard nach der Auswahl deszu materialisierenden Projekts dar:

Maven in Eclipse: m2eclipse

375

Page 395: Maven Definitive Guide De

Figure 14.12. Materialisieren eines Maven Projektes

Beachten Sie, dass die Dialogbox der Maven Artefakte in Figure 14.12,“Materialisieren eines Maven Projektes” (Abbildung 14.12: "Materialisieren einesMaven Projektes") leer ist. Dies ist so, da noch keine Projekte ausgewählt wurden.Um ein Projekt hinzuzufügen müssen Sie auf der rechten Seite den Button

Maven in Eclipse: m2eclipse

376

Page 396: Maven Definitive Guide De

"Hinzufügen" auswählen und einen Abhängigkeit aus dem zentralen MavenRepository auswählen. Dies ist in Figure 14.13, “Auswahl eines Artefakten zumMaterialisieren” (Abbildung 14.13: "Auswahl eines Artefakten zumMaterialisieren"), dargestellt.

Figure 14.13. Auswahl eines Artefakten zum Materialisieren

Maven in Eclipse: m2eclipse

377

Page 397: Maven Definitive Guide De

Auf die Eingabe einer Auswahl hin werden die möglichen Abhängigkeiten imMaven Repository gesucht. Nach wenigen Momenten Indexierung des lokalenRepositories wird eine Liste der möglichen Artefakte wiedergegeben. Wählen Sieden entsprechendne Kandidaten eines Projektes aus, so dass dieser zur Listehinzugefügt werden kann, wie dies in Figure 14.14, “Materialisieren von ApacheCamel” (Abbildung 14.14: "Materialisieren von Apache Camel") gezeigt wird.

Maven in Eclipse: m2eclipse

378

Page 398: Maven Definitive Guide De

Figure 14.14. Materialisieren von Apache Camel

Nach dem hinzufügen der Abhängigkeit bietet Ihnen das m2eclipse dieMöglichkeit alle Projekte des Artefaktes auszuchecken.

14.8. Starten von Maven Buildsm2eclipse wird auch die Menüs „Run as …“ sowie „Debug as … “ anpassen, umIhnen zu ermöglichen Maven Builds zu starten. Figure 14.15, “Starten einesEclipse Builds durch „Run as…“ ” (Abbildung 14.15: "Starten eines Eclipse Buildsdurch „Run as…“ ") stellt das „Run as…“ Menü eines m2eclispse Projektes dar.Ausgehend von diesem Menü können Sie eine Anzahl Maven Lebenszyklusphasenwie z. B. clean, install und package direkt starten. Sie können auch denRun-Konfigurationsdialog aufrufen und dort spezifische Maven Buildparameterund/oder weitere Optionen setzen.

Maven in Eclipse: m2eclipse

379

Page 399: Maven Definitive Guide De

Figure 14.15. Starten eines Eclipse Builds durch „Run as…“

Beabsichtigen Sie einen Maven Build mit erweiterter Konfiguration zu erstellen, sokönnen Sie Run Configuration… auswählen und einen neuen Maven Builderstellen. Figure 14.16, “Konfiguration eines Maven Builds mittels "RunConfigurations..." ” (Abbildung 14.16: "Konfiguration eines Maven Builds mittels"Run Configurations..." ") stellt den Run Dialog vor

Maven in Eclipse: m2eclipse

380

Page 400: Maven Definitive Guide De

Figure 14.16. Konfiguration eines Maven Builds mittels "RunConfigurations..."

Der “Run Configuration ...” Dialog erlaubt es, mehrere Goals sowie Profiledarzustellen, es werden Auswahlmöglichkeiten wie „Test auslassen (skip test)“oder „Update Snapshots“ und erlaubt Ihnen Parameter ausgehend vom denen desProjektes bis hin zur unterliegenden Java Runtime Umgebung (JRE) oder denUmgebungsvariablen zu setzen. Sie können diese Dialogseite benutzen um jedenbeliebigen massgeschneiderten Maven Build mittels m2eclipse zu starten.

Maven in Eclipse: m2eclipse

381

Page 401: Maven Definitive Guide De

14.9. Mit Maven Projekten arbeitenDas m2eclipse bietet auch eine Reihe Funktionalitäten um mit Maven Projekten zuarbeiten sobald diese in Eclipse geladen sind. Viele dieser Funktionen erleichterndas Arbeiten mit Maven, lassen Sie uns diese näher ansehen: Im vorhergehendenAbschnitt materialisierte ich ein Projekt und wählte ein Unterprojekt von ApacheCamel aus; camel-core. Wie werden die verschiedenen Möglichkeiten anhanddieses Projektes verdeutlichen.

Wählen Sie mit der rechten Maustaste den Menüpunkt Maven auf dem camel-core

Projekt, so können Sie die verfügbaren Maven Funktionen sehen. Figure 14.17,“Verfügbare Maven Funktionalitäten” (Abbildung 14.17: "Verfügbare MavenFunktionen") stellt dies dar:

Maven in Eclipse: m2eclipse

382

Page 402: Maven Definitive Guide De

Figure 14.17. Verfügbare Maven Funktionalitäten

Notice in Figure 14.17, “Verfügbare Maven Funktionalitäten” the available Mavenfeatures for the camel-core project, including:

Beachten Sie in Figure 14.17, “Verfügbare Maven Funktionalitäten” (Abbildung14.17: "Verfügbare Maven Funktionalitäten") die verfügbaren Möglichkeiten descamel-core Projektes, diese umfassen

Maven in Eclipse: m2eclipse

383

Page 403: Maven Definitive Guide De

• Hinzufügen von Abhängigkeiten sowie Plugins

• Updaten von Abhängigkeiten, Snapshots sowie Quellverzeichnissen

• Erstellen eines Maven Moduls

• Herunterladen von Quelldateien

• Öffnen von Projekt URLs wie z. B. der Projekt Web Page, dem IssueTracker, SCM oder Continuous Integration Werkzeugen.

• Zuschalten/Abschalten der Workspace Auflösung, verschachteltem Modul-und Abhängigkeitsmanagement

Diese Funktionalitäten können uns Zeit sparen, und so lassen Sie uns diese genaueransehen.

14.9.1. Zufügen und Updaten von Abhängigkeiten undPluginsNehmen wir einmal an, wir wollen zum camel-core POM eine Abhängigkeit oderein Plugin hinzufügen. Der Demonstration halber fügen wir commons-lang alsAbhängigkeit hinzu. (Da die Funktionalität zum Hinzufügen von Abhängigkeitenund Plugins genau dieselbe ist, werden wir beispielhaft lediglich eine Abhängigkeithinzufügen).

m2eclipse bietet zwei Möglichkeiten eine Abhängigkeit dem Projekt zuzufügen.Die erste ist, die pom.xml-Datei direkt durch hinzufügen des entsprechenden XMLElements abzuändern. Der Nachteil dieses Ansatzes ist, dass Sie bereits dieInformationen bezüglich der Artefakte im Detail wissen müssen, oder alternativ dieFunktionen welche im nächsten Abschnitt dargestellt werden benutzen müssten umdiese in den Repository Indexen zu finden. Der Vorteil ist, dass nach einermanuellen Änderung und Speicherung des Projekt POM der MavenAbhängigkeits-Container automatisch die Neuerungen reflektiert. Figure 14.18,“Manuelles Zufügen einer Abhängigkeit zum Projekt POM” (Abbildung 14.18:

Maven in Eclipse: m2eclipse

384

Page 404: Maven Definitive Guide De

"Manuelles Zufügen einer Abhängigkeit zum Projekt POM") zeigt, wie ich eineAbhängigkeit zu commons-lang zum POM der camel-console zugefügt habe undder Maven Abhängigkeits-Container dies automatisch reflektiert und somitdarstellt:

Figure 14.18. Manuelles Zufügen einer Abhängigkeit zum Projekt POM

Nach dem manuelen Zufügen des Elements dependency an das POM, zeigt dieStatusanzeige in der rechten unteren Ecke die Hintergrundaktion wie inFigure 14.19, “Updaten von Maven Abhängigkeiten” (Abbildung 14.19: "Updatenvon Maven Abhängigkeiten") dargestellt, an:

Figure 14.19. Updaten von Maven Abhängigkeiten

Manuelles Zufügen von Abhängigkeiten funktioniert gut, aber benötigt mehrArbeit als dies die zweite Möglichkeit erfordert. Diese Möglichkeit ist vieleinfacher, sie setzt nicht voraus, dass Sie irgendwelche Informationen über die dergroupId der Artefakte hinaus haben. Figure 14.20, “Suchen einer Abhängigkeit”(Abbildung 14.20: "Suchen einer Abhängigkeit") stellt dies dar:

Maven in Eclipse: m2eclipse

385

Page 405: Maven Definitive Guide De

Figure 14.20. Suchen einer Abhängigkeit

Durch eintragen einer groupId in das Abfragefeld durchsucht m2eclipse die Indexeder Repositorien sowie die Versionen der Artefakte welche sich bereits im lokalenMaven Repository befinden. Dies ist die bevorzugte Möglichkeit, da es enormeZeitersparnisse birgt. Mit m2eclipse gehört die langwierige Suche durch diezentralen Maven Repositorien der Vergangenheit an.

Maven in Eclipse: m2eclipse

386

Page 406: Maven Definitive Guide De

14.9.2. Erstellen eines Maven Modulesm2eclipse bringt eine enorme Erleichterung zur Erstellung eines verschachteltenUnterprojektes innerhalb eines multi-modularen Maven Projektes. Sollten Sie einübergeordnetes Maven Projekt bearbeiten und Sie wollen ein Unterprojekterstellen, so machen Sie einen Rechts-Click auf dem Projekt, gehen Sie zumMaven Menü und wählen „Neues Maven Modul Projekt". m2eclipse wird Siedurch die Schritte zum Erstellen eines Untermoduls führen, das POM erstellen, daselterliche POM anpassen und in den Bezügen reflektieren. Vor es m2eclipse gabwar es sehr schwierig, innerhalb von Eclipse mit hierarchisch geschachteltenProjektbäumen zu arbeiten. Mittels m2eclipse wurden die darunterliegendenBeziehungen zwischen Über und Unterprojekten in die Entwicklungsumgebungeingebunden.

14.9.3. Herunterladen der Quelldatei(en)Sollte das zentrale Maven Repository eine Quelldatei der Artefakten beinhalten, sokönnen Sie diese mittels Maven herunterladen und in Eclipse bereitstellen. BeimVersuch eine komplexe Anwendung zu Debuggen, ist es manchmal äusserstnützlich, kann man den zu einem bestehenden externen Quellcode einerAbhängigkeit durch einen einfachen Rechts-Click herunterladen und zu debuggen.Wählen Sie diese Funktion, so wird Maven für Sie versuchen dies zu tun. Ist esnicht möglich, so sollten Sie den Verantwortlichen des Projektes daraufaufmerksam machen, dass dieser im zentralen Maven Repository dieentsprechenden Quelldateien hinterlegen.

14.9.4. Öffnen von ProjektseitenDas Maven POM enthält einige sehr nützliche URLs welche einen Entwicklerinteresieren mögen: Diese sind die Projekt Webseite, der Link auf das SCMRepository, eine URL auf eine Continuous Integrations Instanz (Dienst wie z.B.Hudson) sowie eine URL, welche auf das entsprechende Issue Tracking Systemzeigt. Sind diese Variablen im POM gesetzt, so öffnet m2eclipse dieentsprechenden Seiten innerhalb eines Browserfensters.

Maven in Eclipse: m2eclipse

387

Page 407: Maven Definitive Guide De

14.9.5. Auflösen von AbhängigkeitenSie können ein Projekt so konfigurieren, dass dieses die Abhängigkeiten innerhalbIhres Workspace auflöst. Das hat den Nebeneffekt, dass es unter Umständen dasVerhalten ändert wie Maven einen Artefakten findet. Sollte ein Projekt sokonfiguriert sein, dass es Abhängigkeiten innerhalb eines Workspaces auflöst,müssen diese Artefakte nicht in Ihrem lokalen Repository vorhanden sein. Nehmenwir einmal an, Projekt A und Projekt B sind beide im gleichen Eclipse Workspacevorhanden. Ist Workspace Auflösung ausgestellt, so wird die Abhängigkeit nuraufgelöst, wenn die entsprechenden Artefakte in Ihrem lokalen Repositoryaufzufinden sind. Ist Workspace Auflösung hingegen aktiviert, so wird m2eclipsedie Abhängigkeiten innerhalb Ihres Workspaces auflösen. In anderen Worten, istWorkspace Auflösung aktiviert, so müssen lokale Projekte nicht in Ihrem lokalenRepository vorhanden sein um sich aufeinander zu beziehen.

Sie können auch das Abhängigkeitsmanagement ausschalten. Das hat dann denNebeneffekt, dass es m2eclipse anweist, aufzugeben zu versuchen, denProjektklassenpfad zu verwalten, und wird den Maven Abhängigkeits Containeraus Ihrem Projekt entfernen. Sollten Sie diese Option wählen, so sind Sieletztendlich mit der Verwaltung des Projektklassenpfades auf sich gestellt.

14.10. Arbeiten mit den Maven Repositorienm2eclipse bietet auch einige Werkzeuge um die Arbeit mit Maven Repositorien zuerleichtern. Insbesondere stehen Werkzeuge mit folgender Funktionalität bereit:

• Suchen von Artefakten

• Suchen von Java Klassen

• Indexierung von Maven Repositorien

14.10.1. Suchen von Maven Artefakten sowie Java

Maven in Eclipse: m2eclipse

388

Page 408: Maven Definitive Guide De

Klassenm2eclipse fügt dem Navigationsmenü von Eclipse weitere Einträge hinzu, welchedie Suche nach Maven Artefakten sowie Java Klassen erleichtern. DieseFunktionalität wird durch einen Aufruf innerhalb des Menüs ausgelöst, und ist inFigure 14.21, “Suchen von Artefakten und Klassen” (Abbildung 14.21: "Suchenvon Artefakten und Klassen") dargestellt:

Maven in Eclipse: m2eclipse

389

Page 409: Maven Definitive Guide De

Figure 14.21. Suchen von Artefakten und Klassen

Beachten Sie in Figure 14.21, “Suchen von Artefakten und Klassen” (Abbildung14.21: "Suchen von Artefakten und Klassen") im Navigationsmenü von Eclipse dieEinträge mit dem Titel: Öffnen eines Maven POM und Maven Typauswahl. Der

Maven in Eclipse: m2eclipse

390

Page 410: Maven Definitive Guide De

Eintrag Öffnen eines Maven POM erlaubt Ihnen das Maven Repository nach einembestimmten POM zu durchsuchen, wie in Abbildung Figure 14.22, “Suche einesPOM” (14.22: "Suche eines POM") ersichtlich ist:

Maven in Eclipse: m2eclipse

391

Page 411: Maven Definitive Guide De

Figure 14.22. Suche eines POM

Nach der Auswahl eines Artefakts und der Bestätigung mit OK wird innerhalb vonEclipse das POM zur Ansicht und Modifikation geöffnet. Dies ist sehr hilfreich,wenn Sie das POM eines Artefakten zur schnellen Einsicht ansehen wollen.

Der zweite Eintrag im Navigationsmenü von m2eclipse ist „Open Type fromMaven“. Diese Funktion erlaubt es Ihnen in einem entfernten Repository nach JavaKlassen zu suchen. Beim Öffnen des Dialogfensters geben Sie versuchsweiseeinmal ‚factory bean’ ein, und Sie werden sehen wie viele Klassen mit diesemNamen gefunden werden! Dies ist in Figure 14.23, “Durchsuchen eines Repositorynach einer Java Klasse” (Abbildung 14.23: "Durchsuchen eines Repository nacheiner Java Klasse") dargestellt.

Maven in Eclipse: m2eclipse

392

Page 412: Maven Definitive Guide De

Figure 14.23. Durchsuchen eines Repository nach einer Java Klasse

Dies ist eine grosse zeitliche Entlastung, denn bislang bedeutete die Suche nach

Maven in Eclipse: m2eclipse

393

Page 413: Maven Definitive Guide De

einer Java Klasse eine zeitraubende Suche innerhalb der einzelnen Artefakte einesRepositories. Wenn Sie heute eine bestimmt Klasse suchen, starten Sie einfachEclipse, gehen zum Navigationsmenü und suchen nach der entsprechenden Klasse.m2eclipse wird Ihnen eine Liste der Artefakte zurückgeben in welchem dieseKlasse erscheint.

14.10.2. Indizierung von Maven RepositorienDie Maven Index Anzeige erlaubt es Ihnen POMs in entfernten Repositorienanzuwählen und in Eclipse auszuwählen. Um dies zu demonstrieren, wählen SieView > Show View > Other. Geben sie den Begriff „maven“ in die Suchmaske ein.Sie sollten wie in Figure 14.24, “Anzeigen von Maven Indexen im Eclipse View”(Abbildung 14.24: Anzeigen von Maven Indexen im Eclipse View") dargestellt einView der bestehenden Artefakte zur Auswahl gestellt bekommen:

Maven in Eclipse: m2eclipse

394

Page 414: Maven Definitive Guide De

Figure 14.24. Anzeigen von Maven Indexen im Eclipse View

Wählen Sie das entsprechende View aus und bestätigen Sie mit OK. Es wird sichdas Maven Index View wie in Figure 14.25, “Maven Index View” (Abbildung14.25: "Maven Index View") dargestellt öffnen.

Maven in Eclipse: m2eclipse

395

Page 415: Maven Definitive Guide De

Figure 14.25. Maven Index View

Zusätzlich in Figure 14.26, “Auswahl eines POM innerhalb des Index Views”(Abbildung 14.26: "Auswahl eines POM innerhalb des Index Views") dargestelltnach dem manuellen Suchen eines POM:

Maven in Eclipse: m2eclipse

396

Page 416: Maven Definitive Guide De

Figure 14.26. Auswahl eines POM innerhalb des Index Views

Nach der Auswahl des apache-camel Artefakte wird ein Doppelclick dieses inEclipse zur Bearbeitung öffnen.

Diese Funktion erleichtert die Arbeit mit einem entfernten Repository wesentlichund spart darüber hinaus viel Zeit. Nach all der Zeit welche Sie in den vergangenenJahren mit solcherlei Beschäftigungen zugebracht haben – Ansehen des Inhalteseines Repositories in einem Webbrowser, Herunterladen des Artefaktes undanschiessenden Greppens nach Klassen oder POM – sie werden von m2eclipseangenehm überrascht sein.

Maven in Eclipse: m2eclipse

397

Page 417: Maven Definitive Guide De

14.11. Der neue graphische POM EditorDie neuste Inkarnation des m2eclipse Plugin beinhaltet einen graphischen POMEditor, welcher es erlaubt, alle Aspekte eines Projekt POMs in einer einfachenGUI Schnittstelle zu bearbeiten. Um den POM Editor zu öffnen, Klicken Sie aufeine pom.xml Datei. Sollten Sie eine benutzerdefinierte Einstellung eines Editorsgewählt haben, und der graphische POM Editor nicht als Standardeditor bestimmthaben, so können Sie die Alternative „Open with … /Maven POM Editor“ wählenum diesen zu starten. Der POM Editor wird Ihnen dann die in Figure 14.27,“Übersichtsanzeige des POM Editors für das Projekt idiom-core” (Abbildung14.27: "Übersichtsanzeige des POM Editors für das Projekt idiom-core")dargestellte Gesamtübersicht anzeigen.

Eine häufige Klage bezüglich Maven ist, dass Maven den Entwickler dazu zwingtsich des öfteren mit langen, unübesichtlichen und hoch komplexen XMLDokumenten zu befassen, wie diese in einem multi modularen Build vorkommen.Wärend wir, die Autoren davon überzeugt sind, dass dies ein kleiner Preis für alldie Möglichkeiten und Flexibilität welche Maven bietet ist, so erlaubt dergraphische POM Editor die Arbeit mit Maven ohne die das POM untermauerndeXML Struktur überhaupt zu kennen.

Maven in Eclipse: m2eclipse

398

Page 418: Maven Definitive Guide De

Figure 14.27. Übersichtsanzeige des POM Editors für das Projekt idiom-core

Das in Figure 14.27, “Übersichtsanzeige des POM Editors für das Projektidiom-core” (Abbildung 14.27: "Übersichtsanzeige des POM Editors für dasProjekt idiom-core") dargestellte Projekt besitzt einen Artefakten: idiom-core. Esbesteht keine groupId oder version, und Angaben zur SCM Verwaltung sindebenfalls nicht gesetzt. Dies rührt daher, dass idiom-core die allermeistenInformationen vom übergeordneten Projekt idom erbt. Öffnen wir das pom.xml deselterlichen Projekts im graphischen Editor, so würden wir die Anzeige wie inFigure 14.28, “Übersichtsansicht des graphischen Editors des übergeordnetenidiom Projekts” (Abbildung 14.28: "Übersichtsansicht des graphischen Editors desübergeordneten idiom Projekts") sehen.

Das Symbol des "geöffneten Ordners" an zahlreichen Einträgen des POM Editors

Maven in Eclipse: m2eclipse

399

Page 419: Maven Definitive Guide De

zeigt an, dass der entsprechende Eintrag ebenfalls Teil des Eclipse-Workspace ist,und das Jar-Symbol deutet auf Artefakte welche sich auf ein Maven Repositorybeziehen. Sie können einen solche Eintrag doppelklicken um sich dessen POMDatei im POM Editor anzuzeigen. Dies funktioniert für Module, Abhängigkeiten,Plugins sowie weiteren Artefakten, welche entsprechende Maven Artefakteaufweisen. Unterstrichene Titel in den Seiten des POM Editors weisen aufHyperlinks hin, welche benutzt werden können um Maven Artefakte ebenfalls imPOM Editor zu öffnen.

Figure 14.28. Übersichtsansicht des graphischen Editors des übergeordnetenidiom Projekts

In diesem übergeordneten POM ersichtlich die groupId und version. Beachten Siebitte, dass ein Grossteil der Detailinformationen welche im POM von idiom-core

fehlten, hier gesetzt werden. Die Anzeige des POM Editors beschränkt sich aufAngaben welche im jeweiligen POM gemacht wurden und zeigt keine geerbten

Maven in Eclipse: m2eclipse

400

Page 420: Maven Definitive Guide De

Werte. Sollten Sie dennoch das tatsächliche POM des Projektes ansehen wollen, sonutzen Sie die Möglichkeit „tatsächliches POM anzeigen“ von der Toolbar an deroberen rechten Ecke des POM Editors (linke Klammer und Gleichzeichen überdem blauen Maven Projekt M). Es wird Ihnen das tatsächliche POM wie inFigure 14.29, “Tatsächliches POM von idiom-core” (Abbildung 14.29:"Tatsächliches POM von idiom-core") dargestellt, angezeigt.

Figure 14.29. Tatsächliches POM von idiom-core

In der Ansicht „Tatsächliches POM“ von idiom-core wird das ursprüngliche POMmit den POM der ‚Vorfahren’ (übergeordnet (Eltern), darüber (Grosseltern), etc.)ähnlich zum Aufruf ‚mvn help:effective-pom’ verschmolzen und dargestellt. Dadiese Ansicht nun die Kombination vieler verschiedener abhängiger POM ist, istdiese Darstellung schreibgeschützt und Sie werden in dieser Ansicht keineÄnderungen vornehmen können.

In der Ansicht des POM Editors wie in Figure 14.27, “Übersichtsanzeige des POMEditors für das Projekt idiom-core” (Abbildung 14.27: "Übersichtsanzeige des

Maven in Eclipse: m2eclipse

401

Page 421: Maven Definitive Guide De

POM Editors für das Projekt idiom-core") dargestellt, können Sie ebenfalls dieAnsicht des übergeordneten POM aufrufen. Wählen Sie „Übergeordnetes POMöffnen" von der Toolbar an der oberen rechten Ecke des POM Editors (gelber Pfeilnach oben).

Der POM Editor bietet eine Anzahl verschieden Ansichten der Informationen desPOM, im letzten Tab des Editors können Sie die darunterliegende XML-Dateiansehen. Ebenfalls Vorhanden ist ein Tab bezüglich Abhängigkeiten, dargestellt inFigure 14.30, “Abhängigkeitsansicht des POM Editors” (Abbildung 14.30:"Abhängigkeitsansicht des POM Editors"). Dieses gibt Ihnen eine einfacheMöglichkeit Abhängigkeiten zu verwalten, sowie den AbschnittdependencyManagement des POM zu editieren. Die Abhängigkeitsverwaltungintegriert ebenfalls die Artefakt Suchfunktion des m2eclipse Plugins. BeimArbeiten in den Feldern der Abhängigkeiten können Sie auf Aktionen aus denEditorfeldern oder auch Ctrl + Leertaste Hilfestellungen zurückgreifen.

Müssen Sie einmal weitere Details über bestimmte Artefakte in Erfahrung bringen,so können Sie die Funktion „Webseite öffnen von innerhalb des Abhängigkeitenaufrufen, um eine entsprechenden Webseite aufzurufen.

Maven in Eclipse: m2eclipse

402

Page 422: Maven Definitive Guide De

Figure 14.30. Abhängigkeitsansicht des POM Editors

Der Build Tab, dargestellt in Figure 14.31, “Build Tab des POM Editors”(Abbildung 14.31: "Build Tab des POM Editors") gibt Ihnen Zugriff auf denAbschnitt der Build Elemente. Aus diesem Tab heraus können Sie die benutztenVerzeichnisse, Erweiterungen sowie Standard Goal-Namen sowieRessource-Verzeichnisse bestimmen.

Maven in Eclipse: m2eclipse

403

Page 423: Maven Definitive Guide De

Figure 14.31. Build Tab des POM Editors

Wir haben hier nur eine Untermenge der Funktionalität des POM Editorsaufgezeigt. Sollten Sie an weiteren Informationen interessiert sein, so laden Siesich doch bitte das m2eclipse Plugin herunter und installieren Sie es.

14.12. Projektabhängigkeiten mit m2eclipseanalysierenDie neueste Version des m2eclipse Plugin umfasst unter anderem auch eine EditorVersion mit zusätzlichen Abhängigkeitsanalyse-Werkzeugen. Diese werden dieArt, wie Abhängigkeiten verwaltet werden, grundsätzlich ändern. Eine der

Maven in Eclipse: m2eclipse

404

Page 424: Maven Definitive Guide De

grundlegenden Eigenschaften warum Maven zum Einsatz kommt ist die Art undWeise wie Abhängigkeiten verwaltet werden. Nehmen Sie an, Sie schreiben aneiner Applikation welche auf der Spring Hibernate3 Integration aufbaut, alleswas Sie tun müssen ist, sich auf den Artefakten spring-hibernate3 des zentralenMaven Repository abzustützen. Maven wird dieses Artefakt einlesen und alleweiteren transitiven Abhängigkeiten zufügen. Während dies eine Funktionalität ist,welche viele Anwender zum Einsatz von Maven lockt, so kann es eben auchverwirrend werden, sobald ein Projekt auf dutzenden von Abhängigkeiten aufbaut,und diese jeweils weitere duzende Transitive Abhängigkeiten mit sich bringen.

Probleme treten vor allem dann auf, wenn Sie von einem Projekt abhängen, dessenPOM uvollständig ist, insbesondere wenn Abhängigkeiten nicht ausreichenddefiniert und als optional gekennzeichnet sind, oder sobald es Konflikte zwischentransitiven Abhängigkeiten gibt. Im Falle, dass eine der Voraussetzungen derAusschluss von commons-logging oder des Servlet API ist, oder im Falle dass Sieherausfinden müssen warum in einem bestimmten Gültigkeitsbereich einebestimmte Abhängigkeit auftritt, werden Sie regelmässig gezwungen sein, auf dieKommandozeilen Werkzeuge dependency:tree und dependency:resolve

zurückzugreifen.

Das sind die Momente, in denen Ihnen der neue POM Editor des m2eclipse Pluginzur Hilfe kommt. Wenn Sie ein Projekt mit vielen Abhängigkeiten öffnen, sowerden Sie die zweispaltige Darstellung der Abhängigkeiten wie in Figure 14.32,“Baum der Abhängigkeiten dargestellt im POM Editor” (Abbildung 14.32: "Baumder Abhängigkeiten dargestellt im POM Editor") sehen. Die linke Spalte derAnzeige veranschaulicht den Baum der Abhängigkeiten. Die erste Ebene stellthierbei die direkten Abhängigkeiten zu Ihrem Projekt dar. Jede weitere Ebene istdabei die nächste Stufe der Hierarchie. Die linke Spalte hingegen gibt Ihnen eineinfaches Werkzeug zur Hand um herauszufinden wie eine bestimmteAbhängigkeit in Ihr Projekt gefunden hat. Die rechte Anzeige Zeigt Ihnen alleaufgelösten Abhängigkeiten an; das stellt die tatsächliche Liste der Abhängigkeitendar, nach der Auflösung aller Konflikte sowie Anwendung allerGültigkeitsbereiche und stellt somit die Liste der Abhängigkeiten dar, wie Sie IhrProjekt zur Kompilierung, Test und Paketierung benutzen wird.

Maven in Eclipse: m2eclipse

405

Page 425: Maven Definitive Guide De

Figure 14.32. Baum der Abhängigkeiten dargestellt im POM Editor

Die Funktionalität welche den Tab Abhängigkeiten so wertvoll macht ist, dass esbei Nachforschungen nachvollziebar ist, wie eine bestimmte Abhängigkeit in dieListe der Abhängigkeiten gekommen ist. Such- und Filter- Funktionalitäten welchedem Editor zur Verfügung stehen machen es ein leichtes die Projektabhängigkeitenzu bearbeiten. Sie können das Suche Feld der Eingabefelder sowie die AktivitätenSortieren und Filtern der Abhängigkeits-Hierarchie und der AufgelöstenAbhängigkeiten einsetzen, um sich durch die Abhängigkeiten zu hangeln.Figure 14.33, “Lokalisieren von Abhängigkeiten im Abhängigkeitsbaum”(Abbildung 14.33: "Lokalisieren von Abhängigkeiten im Abhängigkeitsbaum")stellt dar, was passiert, wenn man die Abhängigkeit commons-logging in der Listeder Aufgelösten Abhängigkeiten auswählt. Sollte Filterung auf derAbhängigkeitshierarchie der linken Seite eingestellt sein, so wird die Auswahleiner Abhängigkeit auf der rechten Seite automatisch alle die Knoten desAbhängigkeitsbaumes anzeigen, welche zu deren Inklusion geführt haben. Sollte

Maven in Eclipse: m2eclipse

406

Page 426: Maven Definitive Guide De

Sie versuchen eine bestimmte Abhängigkeit loszuwerden, so wird Ihnen diesesWerkzeug helfen herauszufinden, welche Abhängigkeiten (eben auch transitiveAbhängigkeiten) dazu beitragen, dass eine bestimmte Abhängigkeit aufgelöstwurde. In anderen Worten, sollten Sie einen Artefakten wie z.B. commons-loggingvon Ihrer Liste der Abhängigkeiten löschen wollen, werden Sie hierzu dasAbhängigkeits-Werkzeug benutzen wollen.

Figure 14.33. Lokalisieren von Abhängigkeiten im Abhängigkeitsbaum

Das m2eclipse Plugin bietet Ihnen des Weiteren auch die Möglichkeit den Baumder Abhängigkeiten graphisch darzustellen. Figure 14.34, “Darstellung einesProjektes als Graphen” (Abbildung 14.34: "Darstellung eines Projektes alsGraphen"), stellt die Abhängigkeiten des idiom-core Projekts dar. Die alleroberste Box stellt das idiom-core Projekt dar, alle darunterliegendenAbhängigkeiten sind sinngemäss darunter dargestellt. Direkte Abhängigkeiten sinddurch direkte Verbindungen gekennzeichnet, transitive Abhängigkeiten gehen von

Maven in Eclipse: m2eclipse

407

Page 427: Maven Definitive Guide De

andren Abhängigkeiten aus. Sie können jede beliebige Abhängigkeit auswählen,um die Verbindungen hervorzuheben, oder sie können mit dem Suche Werkzeugim oberen Bereich der Seite die entsprechenden Knoten finden.

Beachten Sie bitte auch das Symbol des “offenen Ordners” für diejenigenArtefakte welche lokal vorliegen, und das Symbol der Jar Datei für Artefaktewelche auf ein Maven-Repository zeigen.

Figure 14.34. Darstellung eines Projektes als Graphen

Der dargestellt Graph kann durch Rechtsklicken der Maustaste geändert werden.Sie können ebefalls Einstellen, ob Sie die artifactId, groupId, version sowieGültigkeitsbereich dargestellt haben möchten, und ob der Text in der Zeileumgebrochen werden soll.Figure 14.35, “Radiale Darstellung desAbhängigkeitsgraphen” (Abbildung 14:35 "Radiale Darstellung desAbhängigkeitsgraphen") stellt den gleichen Graphen aus Figure 14.34,“Darstellung eines Projektes als Graphen” (Abbildung 14.34) in einem anderenLayout dar.

Maven in Eclipse: m2eclipse

408

Page 428: Maven Definitive Guide De

Figure 14.35. Radiale Darstellung des Abhängigkeitsgraphen

14.13. Maven EinstellungenDie Möglichkeit, Maven Grundeinstellungen sowie einige Optionen zu setzen, istein wichtiger Aspekt an der Arbeit mit Maven. m2eclipse bietet die Möglichkeitdiese Einstellungen in der Maven Einstellungen Seite innerhalb von Eclipse zusetzen. Typischerweise, beim Einsatz von Maven von der Befehlszeile, werdendiese Einstellungen als Optionen übergeben oder aus dem Verzeichnis ~/.m2ausgelesen. m2eclipse bietet Zugriff auf die wichtigsten Einstellungen voninnerhalb der IDE. Figure 14.36, “Maven Einstellungen in Eclipse” (Abbildung14.36 "Maven Einstellungen in Eclipse") stellt die Einstellungsseite innerhalb von

Maven in Eclipse: m2eclipse

409

Page 429: Maven Definitive Guide De

Eclipse dar:

Figure 14.36. Maven Einstellungen in Eclipse

Die Tick-Boxen im oberen Bereich erlauben:

• Maven im Offline Modus zu betreiben, d.h. ohne Zugriff auf entfernteRepositorien

• Debug Ausgaben auf die Maven Konsole umleiten

• Quell-Jar Dateien von Artefakten aus entfernten Repositorien herunterladen

• JavaDoc von Artefakten aus entfernten Repositorien herunterladen

• Indexe von entfernten Repositorien beim Start herunterladen undaufzufrischen.

Der nächste Abschnitt erlaubt in einem Fenster die Auswahl der auszuführenden

Maven in Eclipse: m2eclipse

410

Page 430: Maven Definitive Guide De

Goals wann immer ein Projekt importiert wird und auch sobald Quellverzeichnisseeines gegebenen Projektes geändert werden. Das Standard Goal istprocess-ressources, welches die Ressourcen eines Projektes in dasZielverzeichnis kopiert und das Projekt zur Paketierung vorbereitet. DieseAuswahl anzupassen ist hilfreich, sollten sie benutzerdefinierte Goals aufrufen umRessourcen zu verarbeiten oder unterstützende Konfigurationen zu generieren.

Sollten Sie Hilfe brauchen, ein bestimmtes Goal auszuwählen, so drücken sie denSelect … Auswahlknopf um den „Goals“ Dialog aufzurufen. Das linke Fenster ausFigure 14.37, “Maven Goal Dialogfenster” (Abbildung 14.37: "Maven GoalDialogfenster") gibt die Auflistung aller Maven Standardlebenszyklen wieder.

Maven in Eclipse: m2eclipse

411

Page 431: Maven Definitive Guide De

Figure 14.37. Maven Goal Dialogfenster

Es ist gut möglich, dass Sie beim Anblick der vielen verfügbaren Goals überwältigtsind. Es bestehen buchstäblich hunderte Maven Plugins für alles Mögliche, von derGenerierung der Datenbank Struktur, über das Durchführen von Integrationstests,

Maven in Eclipse: m2eclipse

412

Page 432: Maven Definitive Guide De

das Durchführen von statischen Analysen bis hin zur Generierung von WebServices mittels XFire. Der Goals-Dialog bietet mehr als 200 Plugins mitauswählbaren Goals. Das Fenster der rechten Seite der Figure 14.37, “Maven GoalDialogfenster” (Abbildung 14.37: "Maven Goal Dialogfenster") stellt z.B. dieangebotenen Goals des Tomcat Maven Plugin dar. Mittels der Maske Suchenkönnen Sie wie gewohnt die Liste reduzieren. m2eclipse wird während Sie dieEingabe vornehmen die Liste gemäss Ihrer Auswahl anpassen.

Eine weitere Option ist die Anzeige der Maven Installationen Konfiguration wiediese in Figure 14.38, “Maven Installationen Auswahl Anzeige” (Abbildung 14.38:"Maven Installationen Auswahl Anzeige") dargestellt ist:

Figure 14.38. Maven Installationen Auswahl Anzeige

Maven in Eclipse: m2eclipse

413

Page 433: Maven Definitive Guide De

Auf dieser Seite können Sie weitere Maven Installationen Ihrer Eclipse Umgebungzufügen. Sollten Sie eine von der mitgelieferten verschiedene Maven Versionbenutzen wollen, so können Sie auf dieser Seite verschiedene Versionenkonfigurieren. Die Funktionalität ist ähnlich der, der Java Virtuellen Maschine(JVM) Konfiguration innerhalb von Eclipse. Eine eingebettete Version von Maven,bekannt unter dem Titel Maven Embedder ist bereits gesetzt. Diese wird benutztum Maven innerhalb von Eclipse auszuführen. Sollten Sie eine andere Installationneben der von Embedder angebotenen einsetzen wollen, können Sie diese mittelsdes Add Buttons zufügen. Abbildung Figure 14.38, “Maven InstallationenAuswahl Anzeige” (14.38: "Maven Installationen Auswahl Anzeige") zeigt dieKonfiguration mit Maven Embedder, Maven 2.0.9 sowie Maven 2.1-SNAPSHOT.

Die Maven Installationen Auswahl Anzeige erlaubt Ihnen ebenfalls die Dateien derlokalen sowie globalen Einstellungen, der settings.xml Dateien zu setzen.Sollten Sie den Ort dieser Dateien nicht spezifizieren, so geht Maven davon aus,das es die globalen Einstellungen unter conf/settings.xml der ausgewähltenMaven Installation findet. Sie können ebenfalls den Ort der Datei derbenutzerdefinierten Einstellungen von ~/.m2/settings.xml umdefinieren, sowieden Ort des lokalen Maven Repository unterschiedlich von ~/.m2/repositroy

definieren.

Ebenfalls zur Auswahl in den Einstellungen von Eclipse steht die MöglichkeitMaven Version Dekoratoren einzustellen. Diese Einstellung gibt die Möglichkeitdie aktuelle Version eines Artefakten im Eclipse Package Explorers dazustellenund wird in Figure 14.39, “Einstellen der Maven Versions-Dekoratoren”(Abbildung 14.39 "Einstellen der Maven Versions-Dekoratoren") dargestellt.

Maven in Eclipse: m2eclipse

414

Page 434: Maven Definitive Guide De

Figure 14.39. Einstellen der Maven Versions-Dekoratoren

Um diese Einstellung zu aktivieren, wählen Sie einfach den Maven VersionDekorator Tick wie in Figure 14.39, “Einstellen der Maven Versions-Dekoratoren”(Abbildung 14.39: "Einstellen der Maven Versions-Dekoratoren") dargestellt aus.

Maven in Eclipse: m2eclipse

415

Page 435: Maven Definitive Guide De

Sollte dies nicht ausgewählt sein, so wird das Projekt lediglich mit dem Namensowie dem relativen Pfad innerhalb des Package Explorers dargestellt, wie inFigure 14.40, “Package Explorer ohne Maven Versions-Dekoratoren” (Abbildung14.40 "Package Explorer ohne Maven Versions-Dekoratoren") zu sehen:

Figure 14.40. Package Explorer ohne Maven Versions-Dekoratoren

Sobald die Maven Versions-Dekoratoren zugeschaltet sind, wird demProjektnamen die aktuelle Projektversion zugefügt wie dies in Figure 14.41,“Package Explorer mit zugeschalteten Maven Versions-Dekoratoren” (Abbildung14.41: "Package Explorer mit zugeschalteten Maven Versions-Dekoratoren"):

Maven in Eclipse: m2eclipse

416

Page 436: Maven Definitive Guide De

Figure 14.41. Package Explorer mit zugeschalteten MavenVersions-Dekoratoren

Diese nützliche Funktion erlaubt doch auf einen Blick die eingebundenen ProjektVersionen zu überblicken, ohne den Umweg über das POM einzuschlagen.

14.14. Zusammenfassungm2eclipse ist mehr als nur ein Plugin welches die Unterstützung für Maveninnerhalb von Eclipse bereitstellt. Vielmehr ist es eine umfassende Integrationwelche den Umgang beginnend mit dem Erstellen eines Projektes bis hin zumHerausfinden von impliziten Abhängigkeiten um Längen vereinfacht. m2eclipse istein erster Schritt in die Richtung einer Entwicklungsumgebung, welche den

Maven in Eclipse: m2eclipse

417

Page 437: Maven Definitive Guide De

reichen semantischen Fundus des zentralen Maven Repositorien nutzt. Mit derVerbreitung des m2eclipse Plugin werden viele weitere Archetypen veröffentlichtwerden, und weitere Projekte werden Wert darin sehen, deren Artefakte inszentrale Maven Repository zu publizieren. Sollten Sie bereits zuvor versucht habenEclipse und Maven - ohne die Hilfe eines Werkzeuges welches versteht mit denhierarchischen Abhängigkeiten umzugehen – zu benutzen, so können Sieeinschätzen wie wichtig und zentral die saubere Integration ist, welche einArbeiten mit verschachtelten Projekten ermöglicht, welche multi-modulareProjekte mit sich bringen.

Maven in Eclipse: m2eclipse

418

Page 438: Maven Definitive Guide De

Chapter 15. Site Generation

15.1. IntroductionSuccessful software applications are rarely produced by a team of one. When we'retalking about any software worth writing, we're usually dealing teams ofcollaborating developers ranging anywhere in size from a handful of programmersworking in a small team to hundreds or thousands of programmers working inlarge distributed environment. Most open source projects (such as Maven) succeedor fail based on the presence or absence of well written documentation for awidely-distributed, ad-hoc collection of users and developers. In all environmentsit is important for projects to have an easy way to publish and maintain onlinedocumentation. Software development is primarily an exercise in collaboration andcommunication, and publishing a Maven site is one way to make sure that yourproject is communicating with your end-users.

A web site for an open source project is often the foundation for both the end-userand developer communities alike. End-users look to a project's web site fortutorials, user guides, API documentation, and mailing list archives, anddevelopers look to a project's web site for design documents, code reports, issuetracking, and release plans. Large open-sources projects may be integrated withwikis, issue trackers, and continuous integration systems which help to augment aproject's online documentation with material that reflects the current status ofongoing development. If a new open source project has an inadequate web sitewhich fails to convey basic information to prospective users, if often is a sign thatthe project in question will fail to be adopted. In other words, for an open sourceproject, the site and the documentation are as important to the formation of acommunity as the code itself.

Maven can be used to create a project web site to capture information which isrelevant to both the end-user and the developer audience. Out of the box, Mavencan generate reports on everything from unit test failures to package coupling toreports on code quality. Maven provides you with the ability to write simple web

419

Page 439: Maven Definitive Guide De

pages and render those pages against a consistent project template. Maven canpublish site content in multiple formats including XHTML and PDF. Maven can beused to generate API document and can also be used to embedded Javadoc andsource code in your project's binary release archive. Once you've used Maven togenerate all of your project's end-user and developer documentation, you can thenuse Maven to publish your web site to a remote server.

15.2. Building a Project Site with MavenTo illustrate the process of building a project website, create a sample Mavenproject with the archetype plugin:

$ mvn archetype:create -DgroupId=org.sonatype.mavenbook -DartifactId=sample-project

This creates the simplest possible Maven project with a one Java class insrc/main/java and a simple POM. You can then build a Maven site by simplyrunning mvn site. To build the site and preview the result in a browser, you can runmvn site:run, this will build the site and start an embedded instance of Jetty.

$ cd sample-project$ mvn site:run[INFO] Scanning for projects...[INFO] Searching repository for plugin with prefix: 'site'.[INFO] ------------------------------------------------------------------------[INFO] Building sample-project[INFO] task-segment: [site:run] (aggregator-style)[INFO] ------------------------------------------------------------------------[INFO] Setting property: classpath.resource.loader.class =>

'org.codehaus.plexus.velocity.ContextClassLoaderResourceLoader'.[INFO] Setting property: velocimacro.messages.on => 'false'.[INFO] Setting property: resource.loader => 'classpath'.[INFO] Setting property: resource.manager.logwhenfound => 'false'.[INFO] [site:run]2008-04-26 11:52:26.981::INFO: Logging to STDERR via org.mortbay.log.StdErrLog[INFO] Starting Jetty on http://localhost:8080/2008-04-26 11:52:26.046::INFO: jetty-6.1.52008-04-26 11:52:26.156::INFO: NO JSP Support for /, did not find

org.apache.jasper.servlet.JspServlet2008-04-26 11:52:26.244::INFO: Started [email protected]:8080

Once Jetty starts and is listening to port 8080, you can see the project's site whenyou go to http://localhost:8080/ in a web browser. You can see the results in

Site Generation

420

Page 440: Maven Definitive Guide De

Figure 15.1, “Simple Generated Maven Site”.

Figure 15.1. Simple Generated Maven Site

If you click around on this simple site, you'll see that it isn't very helpful as a realproject site. There's just nothing there (and it doesn't look very good). Since thesample-project hasn't configured any developers, mailing lists, issue trackingproviders, or source code repositories, all of these pages on the project site willhave no information. Even the index page of the site states, "There is currently nodescription associated with this project". To customize the site, you'll have to startadd content to the project and to the project's POM.

If you are going to use the Maven Site plugin to build your project's site, you'llwant to customize it. You will want to populate some of the important fields in thePOM that tell Maven about the people participating in the project, and you'll wantto customize the left-hand navigation menu and the links visible in the header ofthe page. To customize the contents of the site and affect the contents of theleft-hand navigation menu, you will need to edit the site descriptor.

Site Generation

421

Page 441: Maven Definitive Guide De

15.3. Customizing the Site DescriptorWhen you add content to the site, you are going to want to modify the left-handnavigation menu that is generated with your site. The following site descriptorcustomizes the logo in the upper left-hand corner of the site. In addition tocustomizing the header of the site, this descriptor adds a menu section to theleft-hand navigation menu under the heading "Sample Project". This menucontains a single link to an overview page.

Example 15.1. An Initial Site Descriptor

<project name="Sample Project"><bannerLeft>

<name>Sonatype</name><src>images/logo.png</src><href>http://www.sonatype.com</href>

</bannerLeft><body>

<menu name="Sample Project"><item name="Overview" href="index.html"/>

</menu><menu ref="reports"/>

</body></project>

This site descriptor references one image. This logo.png image should be placedin ${basedir}/src/site/resources/images. In addition to the change to the sitedescriptor, you'll want to create a simple index.apt page in${basedir}/src/site/apt. Put the following content in index.apt, it will betransformed to the index.html and serve as the first page a user sees when theycome to your project's Maven-generated web site.

Welcome to the Sample Project, we hope you enjoy your timeon this project site. We've tried to assemble somegreat user documentation and developer information, andwe're really excited that you've taken the time to visitthis site.

What is Sample Project

Well, it's easy enough to explain. This sample project is

Site Generation

422

Page 442: Maven Definitive Guide De

a sample of a project with a Maven-generated site fromMaven: The Definitive Guide. A dedicated team of volunteershelp maintain this sample site, and so on and so forth.

To preview the the site, run mvn clean site followed by mvn site:run:

$ mvn clean site$ mvn site:run

Once you do this, load the page in a browser by going to http://localhost:8080. Youshould see something similar to the screenshot in Figure 15.2, “CustomizedSample Project Web Site”.

Figure 15.2. Customized Sample Project Web Site

15.3.1. Customizing the Header GraphicsTo customize the graphics which appear in the upper left-hand and right-hand

Site Generation

423

Page 443: Maven Definitive Guide De

corners of the page, you can use the bannerLeft and bannerRight elements in asite descriptor.

Example 15.2. Adding a Banner Left and Banner Right to Site Descriptor

<project name="Sample Project">

<bannerLeft><name>Left Banner</name><src>images/banner-left.png</src><href>http://www.google.com</href>

</bannerLeft>

<bannerRight><name>Right Banner</name><src>images/banner-right.png</src><href>http://www.yahoo.com</href>

</bannerRight>...

</project>

Both the bannerLeft and bannerRight elements take name, src, and href childelements. In the site descriptor shown above, the Maven Site plugin will generate asite with banner-left.png in the left-hand corner of the page and banner-right inthe right-hand corner of the page. Maven is going to look in${basedir}/src/site/resources/images for these images.

15.3.2. Customizing the Navigation MenuTo customize the contents of the navigation menu, use the menu element with item

child elements. The menu element adds a section to the left-hand navigation menu.Each item is rendered as a link in that menu.

Example 15.3. Creating Menu Items in a Site Descriptor

<project name="Sample Project">...<body>

<menu name="Sample Project"><item name="Introduction" href="index.html"/>

Site Generation

424

Page 444: Maven Definitive Guide De

<item name="News" href="news.html"/><item name="Features" href="features.html"/><item name="Installation" href="installation.html"/><item name="Configuration" href="configuration.html"/><item name="FAQ" href="faq.html"/>

</menu>...

</body></project>

Menu items can also be nested. If you nest items, you will be creating a collapsiblemenu in the left-hand navigation menu. The following example adds a link"Developer Resources" which links to /developer/index.html. When a user islooking at the Developer Resources page, the menu items below the DeveloperResources menu item will be expanded.

Example 15.4. Adding a Link to the Site Menu

<project name="Sample Project">...<body>

...<menu name="Sample Project">...<item name="Developer Resources" href="/developer/index.html"

collapse="true"><item name="System Architecture" href="/developer/architecture.html"/><item name="Embedder's Guide" href="/developer/embedding.html"/>

</item></menu>...

</body></project>

When an item has the collapse attribute set to true, Maven will collapse the itemuntil a user is viewing that specific page. In the previous example, when the user isnot looking at the Developer Resources page, Maven will not display the SystemArchitecture and Embedder's Guide links; instead, it will display an arrow pointingto the Developer Resources link. When the user is viewing the DeveloperResources page it will show these links with an arrow pointing down.

Site Generation

425

Page 445: Maven Definitive Guide De

15.4. Site Directory StructureMaven places all site document under src/site. Documents of similar format areplaced in subdirectories of src/site. All APT documents should be insrc/site/apt, all FML documents should be in src/site/fml, and XDocdocuments should be in src/site/xdoc. The site descriptor should be insrc/site/site.xml, and all resources should be stored undersrc/site/resources. When the Maven Site plugin builds a web site, it will copyeverything in the resources directory to the root of the site. If you store an image insrc/site/resources/images/test.png, they you would refer to the image fromyour site documentation using the relative path images/test.png.

The following examples shows the location of all files in a project which containsAPT, FML, HTML, XHTML, and some XDoc. Note that the XHTML content issimply stored in the resources directory. The architecture.html file will not beprocessed by Doxia, it will simply be copied to the output directory. You can usethis approach if you want to include unprocessed HTML content and you don'twant to take advantage of the templating and formatting capabilities of Doxia andthe Maven Site plugin.

sample-project+- src/

+- site/+- apt/| +- index.apt| +- about.apt| || +- developer/| +- embedding.apt|+- fml/| +- faq.fml|+- resources/| +- images/| | +- banner-left.png| | +- banner-right.png| || +- architecture.html| +- jira-roadmap-export-2007-03-26.html|+- xdoc/| +- xml-example.xml

Site Generation

426

Page 446: Maven Definitive Guide De

|+- site.xml

Note that the developer documentation is stored insrc/site/apt/developer/embedding.apt. This extra directory below the apt

directory will be reflected in the location of the resulting HTML page on the site.When the Site plugin renders the contents of the src/site/apt directory it willproduce HTML output in directories relative to the site root. If a file is in the aptdirectory it will be in the root directory of the generated web site. If a file is in theapt/developer directory it will be generated in the developer/ directory of theweb site.

15.5. Writing Project DocumentationMaven uses a documentation-processing engine called Doxia which reads multiplesource formats into a common document model. Doxia can then manipulatedocuments and render the result into several output formats, such as PDF orXHTML. To write document for your project, you will need to write your contentin a format which can be parsed by Doxia. Doxia currently has support for AlmostPlain Text (APT), XDoc (a Maven 1.x documentation format), XHTML, and FML(useful for FAQ documents) formats.

This chapter has a cursory introduction to the APT format. For a deeper understandof the APT format, or for an in-depth introduction to XDoc or FML, please see thefollowing resources:

• APT Reference: http://maven.apache.org/doxia/format.html

• XDoc Reference: http://jakarta.apache.org/site/jakarta-site2.html

• FML Reference: http://maven.apache.org/doxia/references/fml-format.html

15.5.1. APT ExampleExample 15.5, “APT Document” shows a simple APT document with an

Site Generation

427

Page 447: Maven Definitive Guide De

introductory paragraph and a simple list. Note that the list is terminated by thepsuedo-element "[]".

Example 15.5. APT Document

---Introduction to Sample Project---Brian Fox---26-Mar-2008---

Welcome to Sample Project

This is a sample project, welcome! We're excited that you've decided toread the index page of this Sample Project. We hope you enjoy the simplesample project we've assembled for you.

Here are some useful links to get you started:

* {{{news.html}News}}

* {{{features.html}Features}}

* {{{faq.html}FAQ}}

[]

If the APT document from Example 15.5, “APT Document” were placed insrc/site/apt/index.apt, the Maven Site plugin will parse the APT using Doxiaand produce XHTML content in index.html.

15.5.2. FML ExampleMany projects maintain a Frequently Asked Questions (FAQ) page. Example 15.6,“FAQ Markup Language Document” shows an example of an FML document.

Example 15.6. FAQ Markup Language Document

<?xml version="1.0" encoding="UTF-8"?><faqs title="Frequently Asked Questions">

<part id="General"><faq id="sample-project-sucks">

Site Generation

428

Page 448: Maven Definitive Guide De

<question>Sample project doesn't work. Why does sampleproject suck?</question>

<answer><p>

We resent that question. Sample wasn't designed to work, it wasdesigned to show you how to use Maven. If you really thinkthis project sucks, then keep it to yourself. We're notinterested in your pestering questions.

</p></answer>

</faq><faq id="sample-project-source"><question>I want to put some code in Sample Project,

how do I do this?</question><answer>

<p>If you want to add code to this project, just start puttingJava source in src/main/java. If you want to put some sourcecode in this FAQ, use the source element:

</p><source>

for( int i = 0; i < 1234; i++ ) {// do something brilliant

}</source>

</answer></faq>

</part></faqs>

15.6. Deploying Your Project WebsiteOnce your project's documentation has been written and you've creates a site to beproud of, you will want to deploy it a server. To deploy your site you'll use theMaven Site plugin which can take care of deploying your project's site to a remoteserver using a number of methods including FTP, SCP, and DAV. To deploy thesite using DAV, configure the site entry of the distributionManagement sectionin the POM, like this:

Example 15.7. Configuring Site Deployment

<project>...

Site Generation

429

Page 449: Maven Definitive Guide De

<distributionManagement><site><id>sample-project.website</id><url>dav:https://dav.sample.com/sites/sample-project</url>

</site></distributionManagement>...

</project>

The url in distribution management has a leading indicator dav which tells theMaven Site plugin to deploy the site to a URL that is able to understand WebDAV.Once you have added the distributionManagement section to oursample-project POM, we can try deploying the site:

$ mvn clean site-deploy

If you have a server configured properly that can understand WebDAV, Mavenwill deploy your project's web site to the remote server. If you are deploying thisproject to a site and server visible to the public, you are going to want to configureyour web server to access for credentials. If your web server asks for a usernameand password (or other credentials, you can configure this values in your~/.m2/settings.xml).

15.6.1. Configuring Server AuthenticationTo configure a username/password combination for use during the sitedeployment, we'll include the following in $HOME/.m2/settings.xml:

Example 15.8. Storing Server Authentication in User-specific Settings

<settings>...<servers>

<server><id>sample-project.website</id><username>jdcasey</username><password>b@dp@ssw0rd</password>

</server>...

</servers>...

Site Generation

430

Page 450: Maven Definitive Guide De

</settings>

The server authentication section can contain a number of authentication elements.In the event you're using SCP for deployment, you may wish to use public-keyauthentication. To do this, specify the publicKey and passphrase elements,instead of the password element. You may still want to configure the usernameelement, depending on your server's configuration.

15.6.2. Configuring File and Directory ModesIf you are working in a large group of developers, you'll want to make sure thatyour web site's files end up with the proper user and group permissions after theyare published to the remote server. To configure specific file and directory modesfor use during the site deployment, include the following in$HOME/.m2/settings.xml:

Example 15.9. Configuring File and Directory Modes on Remote Servers

<settings>...<servers>

...<server><id>hello-world.website</id>...<directoryPermissions>0775</directoryPermissions><filePermissions>0664</filePermissions>

</server></servers>...

</settings>

The above settings will make any directories readable and writable by either theowner or members of the owner's primary group; the anonymous users will onlyhave access to read and list the directory. Similarly, the owner or members of theowner's primary group will have access to read and write any files, with the rest ofthe world restricted to read-only access.

Site Generation

431

Page 451: Maven Definitive Guide De

15.7. Customizing Site AppearanceThe default Maven template leaves much to be desired. If you wish to customizeyour project's website beyond simply adding content, navigational elements, andcustom logos. Maven offers several mechanisms for customizing your website thatoffer successively deeper access to content decoration and website structure. Forsmall, per-project tweaks, providing a custom site.css is often enough. However,if you want your customizations to be reusable across multiple projects, or if yourcustomizations involve changing the XHTML that Maven generates, you shouldconsider creating your own Maven website skin.

15.7.1. Customizing the Site CSSThe easiest way to affect the look and feel of your project's web site is through theproject's site.css. Just like any images or XHTML content you provide for thewebsite, the site.css file goes in the src/site/resources directory. Mavenexpects this file to be in the src/site/resources/css subdirectory. With CSS it ispossible to change text styling properties, layout properties, and even addbackground images and custom bullet graphics. For example, if we decided that tomake the menu heading stand out a little more, we might try the following style insrc/site/resources/css/site.css:

#navcolumn h5 {font-size: smaller;border: 1px solid #aaaaaa;background-color: #bbb;margin-top: 7px;margin-bottom: 2px;padding-top: 2px;padding-left: 2px;color: #000;

}

When you regenerate the website, the menu headers should be framed by a graybackground and separated from the rest of the menu by some extra margin space.Using this file, any structure in the Maven-generated website can be decorated withcustom CSS. When you change site.css in a specific Maven project, the changes

Site Generation

432

Page 452: Maven Definitive Guide De

will apply to that specific project. If you are interested in making changes that willapply to more than one Maven project, you can create a custom skin for the MavenSite plugin.

TipThere is no good reference for the structure of the default Maven sitetemplate. If you are attempting to customize the style of your Mavenproject, you should use a Firefox extension like Firebug as a tool toexplore the DOM for your project's pages.

15.7.2. Create a Custom Site TemplateIf the default Maven Site structure just doesn't do it for you, you can alwayscustomize the Maven site template. Customizing the Maven Site template givesyou complete control over the ultimate output of the Maven plugin, and it ispossible to customize your project's site template to the point where it hardlyresembles the structure of a default Maven site template.

The Site plugin uses a rendering engine called Doxia, which in turn uses a Velocitytemplate to render the XHTML for each page. To change the page structure that isrendered by default, we can configure the site plugin in our POM to use a custompage template. The site template is fairly complex, and you'll need to have a goodstarting point for your customization. Start by copying the default Velocitytemplate from Doxia's Subversion repository default-site.vm tosrc/site/site.vm. This template is written in a templating language calledVelocity. Velocity is a simple templating language which supports simple macrodefinition and allows you to access an object's methods and properties using simplenotation. A full introduction is beyond the scope of this book, for more informationabout Velocity and a full introduction please go to the Velocity project site athttp://velocity.apache.org.

The default-site.xml template is fairly involved, but the change required tocustomize the left-hand menu is relatively straightforward. If you are trying tochange the appearance of a menuItem, locate the menuItem macro. It resides in a

Site Generation

433

Page 453: Maven Definitive Guide De

section that looks like this:

#macro ( menuItem $item )

...

#end

If you replace the macro definition with the macro definition listed below, you willinjects Javascript references into each menu item which will allow the reader toexpand or collapse the menu tree without suffering through a full page reload:

#macro ( menuItem $item $listCount )#set ( $collapse = "none" )#set ( $currentItemHref = $PathTool.calculateLink( $item.href,

$relativePath ) )#set ( $currentItemHref = $currentItemHref.replaceAll( "\\", "/" ) )

#if ( $item && $item.items && $item.items.size() > 0 )#if ( $item.collapse == false )#set ( $collapse = "collapsed" )

#else## By default collapsed#set ( $collapse = "collapsed" )

#end

#set ( $display = false )#displayTree( $display $item )

#if ( $alignedFileName == $currentItemHref || $display )#set ( $collapse = "expanded" )

#end#end<li class="$collapse">

#if ( $item.img )#if ( ! ( $item.img.toLowerCase().startsWith("http") ||

$item.img.toLowerCase().startsWith("https") ) )#set ( $src = $PathTool.calculateLink( $item.img, $relativePath ) )#set ( $src = $item.img.replaceAll( "\\", "/" ) )<img src="$src"/>

#else<img src="$item.img" align="absbottom" style="border-width: 0"/>

#end#end#if ( $alignedFileName == $currentItemHref )<strong>$item.name</strong>

#else#if ( $item && $item.items && $item.items.size() > 0 )<a onclick="expand('list$listCount')"

style="cursor:pointer">$item.name</a>#else

Site Generation

434

Page 454: Maven Definitive Guide De

<a href="$currentItemHref">$item.name</a>#end

#end#if ( $item && $item.items && $item.items.size() > 0 )

#if ( $collapse == "expanded" )<ul id="list$listCount" style="display:block">#else<ul id="list$listCount" style="display:none">#end#foreach( $subitem in $item.items )

#set ( $listCounter = $listCounter + 1 )#menuItem( $subitem $listCounter )

#end</ul>

#end</li>

#end

This change adds a new parameter to the menuItem macro. For the newfunctionality to work, you will need to change references to this macro, or theresulting template may produce unwanted or internally inconsistent XHTML. Tofinish changing these references, make a similar replacement in the mainMenu

macro. Find this macro by looking for something similar to the following templatesnippet:

#macro ( mainMenu $menus )...

#end

Replace the mainMenu macro with the following implementation:

#macro ( mainMenu $menus )#set ( $counter = 0 )#set ( $listCounter = 0 )#foreach( $menu in $menus )

#if ( $menu.name )<h5 onclick="expand('menu$counter')">$menu.name</h5>#end<ul id="menu$counter" style="display:block">#foreach( $item in $menu.items )

#menuItem( $item $listCounter )#set ( $listCounter = $listCounter + 1 )

#end</ul>#set ( $counter = $counter + 1 )

#end#end

Site Generation

435

Page 455: Maven Definitive Guide De

This new mainMenu macro is compatible with the new menuItem macro above, andalso provides support for a Javascript-enabled top-level menu. Clicking on atop-level menu item with children will expand the menu and allow users to see theentire tree without waiting for a page to load.

The change to the menuItem macro introduced an expand() Javascript function.This method needs to be added to the main XHTML template at the bottom of thistemplate file. Find the section that looks similar to the following:

<head>...<meta http-equiv="Content-Type"

content="text/html; charset=${outputEncoding}" />...

</head>

and replace it with this:

<head>...<meta http-equiv="Content-Type"

content="text/html; charset=${outputEncoding}" /><script type="text/javascript">function expand( item ) {

var expandIt = document.getElementById( item );if( expandIt.style.display == "block" ) {

expandIt.style.display = "none";expandIt.parentNode.className = "collapsed";

} else {expandIt.style.display = "block";expandIt.parentNode.className = "expanded";

}}

</script>#if ( $decoration.body.head )#foreach( $item in $decoration.body.head.getChildren() )

#if ( $item.name == "script" )$item.toUnescapedString()

#else$item.toString()

#end#end

#end</head>

After modifying the default site template, you'll need to configure your project'sPOM to reference this new site template. To customize the site template, you'll

Site Generation

436

Page 456: Maven Definitive Guide De

need to use the templateDirectory and template configuration properties of theMaven Site plugin.

Example 15.10. Customizing the Page Template in a Project's POM

<project>...<build>

<plugins><plugin>

<artifactId>maven-site-plugin</artifactId><configuration>

<templateDirectory>src/site</templateDirectory><template>site.vm</template>

</configuration></plugin>

</plugins></build>...

</project>

Now, you should be able to regenerate your project website. When you do so youmay notice that the resources and CSS for the maven site are missing. When aMaven project customizes the site template, the Site plugin expects the project tosupply all of the default images and CSS. To seed your project's resources, youmay want to copy the resources from the default Doxia site renderer project to yourown project's resources directory by executing the following commands:

$ svn co \http://svn.apache.org/repos/asf/maven/doxia/doxia-sitetools/\trunk/doxia-site-renderer

$ rm \doxia-site-renderer/src/main/resources/org/apache/maven/\

doxia/siterenderer/resources/css/maven-theme.css$ cp -rf \

doxia-site-renderer/src/main/resources/org/apache/maven/\doxia/siterenderer/resources/* \

sample-project/src/site/resources

Check out the doxia-site-renderer project, remove the default maven-theme.cssfile and then copy all the resources to your project's src/site/resourcesdirectory.

Site Generation

437

Page 457: Maven Definitive Guide De

When you regenerate the site, you'll notice that a few menu items look like regularunstyled text. This is caused by a quirky interaction between the site's CSS and ournew custom page template. It can be fixed by modifying our site.css to restorethe proper link color for these menus. Simply add this:

li.collapsed, li.expanded, a:link {color:#36a;

}

After regenerating the site, the menu's link color should be corrected. If youapplied the new site template to the same sample-project from this chapter, you'llnotice that the menu now consists of a tree. Clicking on "Developer Resources" nolonger takes you to the "Developer Resources" page; in stead, it expands thesub-menu. Since you've turned the Developer Resources menu-item into adynamically-folding sub-menu, you have lost the ability to reach thedeveloper/index.apt page. To address this change, you should add an Overviewlink to the sub-menu which references the same page:

Example 15.11. Adding a Menu Item to a Site Descriptor

<project name="Hello World">...<menu name="Main Menu">

...<item name="Developer Resources" collapse="true"><item name="Overview" href="/developer/index.html"/><item name="System Architecture" href="/developer/architecture.html"/><item name="Embedder's Guide" href="/developer/embedding.html"/>

</item></menu>...

</project>

15.7.3. Reusable Website SkinsIf your organization is created many Maven project sites, you will likely want toreuse site template and CSS customizations throughout an organization. If youwant thirty projects to share the same CSS and site template, you can use Maven's

Site Generation

438

Page 458: Maven Definitive Guide De

support for skinning. Maven Site skins allow you to package up resources andtemplates which can be reused by other projects in lieu of duplicating your sitetemplate for each project which needs to be customized.

While you can define your own skin, you may want to consider using one ofMaven's alternate skins. You can choose from several skins. These each providetheir own layout for navigation, content, logos, and templates:

• Maven Classic Skin - org.apache.maven.skins:maven-classic-skin:1.0

• Maven Default Skin - org.apache.maven.skins:maven-default-skin:1.0

• Maven Stylus Skin - org.apache.maven.skins:maven-stylus-skin:1.0.1You can find an up-to-date and comprehensive listing in the Maven repository:http://repo1.maven.org/maven2/org/apache/maven/skins/.

Creating a custom skin is a simple matter of wrapping your customizedmaven-theme.css in a Maven project, so that it can be referenced by groupId,artifactId, and version. It can also include resources such as images, and areplacement website template (written in Velocity) that can generate a completelydifferent XHTML page structure. In most cases, custom CSS can manage thechanges you desire. To demonstrate, let's create a designer skin for thesample-project project, starting with a custom maven-theme.css.

Before we can start writing our custom CSS, we need to create a separate Mavenproject to allow the sample-project site descriptor to reference it. First, useMaven's archetype plugin to create a basic project. Issue the following commandfrom the directory above the sample-project project's root directory:

$ mvn archetype:create -DartifactId=sample-site-skin-DgroupId=org.sonatype.mavenbook

This will create a project (and a directory) called sample-site-skin. Changedirectories to the new sample-site-skin directory, remove all of the source codeand tests, and create a directory to store your skin's resources:

$ cd sample-site-skin$ rm -rf src/main/java src/test$ mkdir src/main/resources

Site Generation

439

Page 459: Maven Definitive Guide De

15.7.4. Creating a Custom Theme CSSNext, write a custom CSS for the custom skin. A Å file in a Maven site skin shouldbe placed in src/main/resources/css/maven-theme.css. Unlike the site.css

file, which goes in the site-specific source directory for a project, themaven-theme.css will be bundled in a JAR artifact in your local Maven repository.In order for the maven-theme.css file to be included in the skin's JAR file, it mustreside in the main project-resources directory, src/main/resources.

As with the default the default site template, you will want to start customizingyour new skin's CSS from a good starting point. Copy the CSS file used by thedefault Maven skin to your project's maven-theme.css. To get a copy of this themefile, save the contents of maven-theme.css from the maven-default-skin projectto src/main/resources/css/maven-theme.css in our new skin project.

Now that we have the base theme file in place, customize it using the CSS fromour old site.css file. Replace the #navcolumn h5 CSS block with the following:

#navcolumn h5 {font-size: smaller;border: 1px solid #aaaaaa;background-color: #bbb;margin-top: 7px;margin-bottom: 2px;padding-top: 2px;padding-left: 2px;color: #000;

}

Once you've customized the maven-theme.css, build and install thesample-site-skin JAR artifact to your local Maven repository by running:

$ mvn clean install

Once the installation is complete, switch back to the sample-project projectdirectory, if you already customized the site.css earlier in this chapter, movesite.css to site.css.bak so it no longer affects the output of the Maven Siteplugin:

$ mv src/site/resources/css/site.css src/site/resources/css/site.css.bak

Site Generation

440

Page 460: Maven Definitive Guide De

To use the sample-site-skin in the sample-project site, you'll need to add areference to the sample-site-skin artifact in the sample-project's site descriptor.A site references a skin in the site descriptor using the skin element:

Example 15.12. Configuring a Custom Site Skin in Site Descriptor

<project name="Sample Project">...<skin>

<groupId>org.sonatype.mavenbook</groupId><artifactId>sample-site-skin</artifactId>

</skin>...

</project>

You can think of a Maven Site skin as a site dependency. Site skins are referencedas artifacts with a groupId and an artifactId. Using a site skin allows you toconsolidate site customizations to a single project, and makes reusing custom CSSand site templates as easy as reusing build logic through a custom Maven plugin.

15.7.5. Customizing Site Templates in a SkinJust as you can customize a the site CSS in a Maven Site skin, you can alsocustomize the site template. Doxia's site-rendering tools will expect to find a filecalled META-INF/maven/site.vm inside the skin JAR. To incorporate a custompage template, copy the template file into the correct location within thesample-site-skin. Copy the custom site template developed earlier in the chapterto src/main/resources/META-INF/maven in sample-site-skin:

$ mv sample-project/src/site/site.vm \sample-site-skin/src/main/resources/META-INF/maven

If you already customized the site template in sample-project, remove the Siteplugin configuration which pointed to this site template. The Site plugin willrender the site using the site template referenced in the site skin.

<plugin><artifactId>maven-site-plugin</artifactId><configuration>

Site Generation

441

Page 461: Maven Definitive Guide De

<templateDirectory>src/site</templateDirectory><template>site.vm</template>

</configuration></plugin>

A Maven Site skin is expected to include all of the resources it depends on. Thisincludes CSS, images, and logos. If you already customized the site templateearlier in the chapter, you've already copied the default doxia-site-rendererresources to the sample-project's src/site/resources directory. You'll need tomove those files out of the sample-project project and into the newsample-site-skin project by executing the following commands:

$ cd ..$ mkdir -p sample-site-skin/src/main/resources/css$ mv sample-project/src/site/resources/css/maven-base.css \

sample-site-skin/src/main/resources/css$ mkdir -p sample-site-skin/src/main/resources/images$ mv sample-project/src/site/resources/images/logos \

sample-site-skin/src/main/resources/images$ mv sample-project/src/site/resources/images/expanded.gif \

sample-site-skin/src/main/resources/images$ mv sample/src/site/resources/images/collapsed.gif \

sample-site-skin/src/main/resources/images

You've changed the sample-site-skin, so you'll need to install this skin into yourlocal Maven repository. Once you install the skin locally and rebuild thesample-project web site. You'll see that the skin's custom site template wasapplied to the sample-project's web site. You'll notice that the color of the menuitems may be a little off because you haven't added the necessary CSS to thecollapsed and expanded menu items. To do this, modifysrc/main/resources/css/maven-theme.css. Change:

a:link {...

}

to this:

li.collapsed, li.expanded, a:link {...

}

Rebuild the skin, then regenerate the website, and you'll see that the menu items

Site Generation

442

Page 462: Maven Definitive Guide De

have returned to normal. You've successfully created a Maven theme which can beused to apply CSS and templates to a set of projects.

15.8. Tips and TricksThis section lists some useful tips and tricks you can use when creating a Mavensite.

15.8.1. Inject XHTML into HEADTo inject XHTML into the HEAD element, add a head element to the bodyelement in your project's Site descriptor. The following example adds a feed link toevery page in the sample-project web site.

Example 15.13. Injecting HTML into the HEAD element

<project name="Hello World">...<body>

<head><link href="http://sample.com/sites/sample-project/feeds/blog"

type="application/atom+xml"id="auto-discovery"rel="alternate"title="Sample Project Blog" />

</head>...

</body></project>

15.8.2. Add Links under Your Site LogoIf you are working on a project which is being developed by an organization, youmay want to add links under your project's logo. Assume that your project is a partof the Apache Software Foundation, you might want to add a link to the ApacheSoftware Foundation web site right below your logo, and you might want to add a

Site Generation

443

Page 463: Maven Definitive Guide De

link to a parent project as well. To add links below your site logo, just add a linkselement to the body element in the Site descriptor. Each item element in the linkselement will be rendered as a link in a bar directly below your project's logo. Thefollowing example would add a link to the Apache Software Foundation followedby a link to the Apache Maven project.

Example 15.14. Adding Links Under Your Site Logo

<project name="Hello World">...<body>

...<links><item name="Apache" href="http://www.apache.org"/><item name="Maven" href="http://maven.apache.org"/>

</links>...

</body></project>

15.8.3. Add Breadcrumbs to Your SiteIf your hierarchy exists within a logical hierarchy, you may want to place a seriesof breadcrumbs to give the user a sense of context and give them a way to navigateup the tree to projects which might contain the current project as a subproject. Toconfigure breadcrumbs, add a breadcrumbs element to the body element in the sitedescriptor. Each item element will render a link, and the items in the breadcrumbs

element will be rendered in order. The breadcrumb items should be listed fromhighest level to lowest level. In the following site descriptor, the Codehaus itemwould be seen to contain the Mojo item.

Example 15.15. Configuring the Site's Breadcrumbs

<project name="Sample Project">...<body>

...<breadcrumbs><item name="Codehaus" href="http://www.codehaus.org"/>

Site Generation

444

Page 464: Maven Definitive Guide De

<item name="Mojo" href="http://mojo.codehaus.org"/></breadcrumbs>...

</body></project>

15.8.4. Add the Project VersionWhen you are documenting a project that has multiple versions, it is often veryhelpful to list the project's version number on every page. To display your project'sversion on the website, simply add the version element to your site descriptor:

Example 15.16. Positioning the Version Information

<project name="Sample Project">...<version position="left"/>...

</project>

This will position the version (in the case of the sample-project project, it will say"Version: 1.0-SNAPSHOT") in the upper left-hand corner of the site, right next tothe default "Last Published" date. Valid positions for the project version are:

leftLeft side of the bar just below the site logo

rightRight side of the bar just below the site logo

navigation-topTop of the menu

navigation-bottomBottom of the menu

Site Generation

445

Page 465: Maven Definitive Guide De

noneSuppress the version entirely

15.8.5. Modify the Publication Date Format and LocationIn some cases, you may wish to reformat or reposition the "Last Published" datefor your project website. Just like the project version tip above, you can specify theposition of the publication date by using one of the following:

leftLeft side of the bar just below the site logo

rightRight side of the bar just below the site logo

navigation-topTop of the menu

navigation-bottomBottom of the menu

noneSuppress the publication entirely

Example 15.17. Positioning the Publish Date

<project name="Sample Project">...<publishDate position="navigation-bottom"/>...

</project>

By default, the publication date will be formatted using the date format stringMM/dd/yyyy. You can change this format by using the standard notation found inthe JavaDocs for java.text.SimpleDateFormat (see JavaDoc forSimpleDateFormat for more information). To reformat the date using yyyy-MM-dd,

Site Generation

446

Page 466: Maven Definitive Guide De

use the following publishDate element.

Example 15.18. Configuring the Publish Date Format

<project name="Sample Project">...<publishDate position="navigation-bottom" format="yyyy-MM-dd"/>...

</project>

15.8.6. Using Doxia MacrosIn addition to its advanced document rendering features, Doxia also provides amacro engine that allows each input format to trigger injection of dynamic content.An excellent example of this is the snippet macro, which allows a document to pulla code snippet out of a source file that's available via HTTP. Using this macro, asmall fragment of APT can be rendered into XHTML. The following APT codecalls out to the snippet macro. Please note that this code should be on a singlecontinuous line, the black slash character is inserted to denote a line break so thatthis code will fit on the printed page.

%{snippet|id=modello-model|url=http://svn.apache.org/repos/asf/maven/\archetype/trunk/maven-archetype/maven-archetype-model/src/main/\mdo/archetype.mdo}

Example 15.19. Output of the Snippet Macro in XHTML

<div class="source"><pre>

<model><id>archetype</id><name>Archetype</name><description><![CDATA[Maven's model for the archetype descriptor.]]></description><defaults>

<default><key>package</key><value>org.apache.maven.archetype.model</value>

</default></defaults><classes>

Site Generation

447

Page 467: Maven Definitive Guide De

<class rootElement="true" xml.tagName="archetype"><name>ArchetypeModel</name><description>Describes the assembly layout and packaging.</description><version>1.0.0</version><fields>

<field><name>id</name><version>1.0.0</version><required>true</required><type>String</type>

</field>...

</fields></class>

</classes></model>

</pre></div>

WarningDoxia macros MUST NOT be indented in APT source documents. Doingso will result in the APT parser skipping the macro altogether.

For more information about defining snippets in your code for reference by thesnippet macro, see the Guide to the Snippet Macro on the Maven website, athttp://maven.apache.org/guides/mini/guide-snippet-macro.html.

Site Generation

448

Page 468: Maven Definitive Guide De

Chapter 16. Repository Management withNexusThis chapter is deprecated. While the original version of Maven: The DefinitiveGuide had an entire chapter dedicated to the Nexus Repository Manager, Sonatypefound that the content was growing fast enough to deserve its own dedicate titled.We've decided to spin off the content in the Repository Management chapter into anew book called Repository Management with Nexus. This new book covers bothNexus Open Source and Nexus Professional, it guides you through the process ofdownloading and installing a repository manager, and it provides a detailed surveyof all of the configuration options.

Click here to read Repository Management with Nexus.

NoteIn the past, a book was a very static object, it was written, edited,proofread, and printed. After printing there was little time to reconsiderthe structure or content of a book. While the Internet has offered anopportunity for the real-time, constantly evolving on-demand book, thepublishing industry has yet to come to terms with the idea of a book thatis "alive" - a book that can change the day after it was sent to the print,and a book that can split into two and continue to grow. We decided tomake this break because it makes sense, if one of our chapters starts togrow into a hundred-page beast, we're not just going to throw more pagesat an already "thick" reference book. (But then again, this book isn't"thick" if you are reading it in a web browser, it is all about perspective.)

What's true about software development is also true about writing. If youstart out a project with a single, monolithic project, there is going tocome a time when it makes good sense to refactor a package or collectionof classes into a separate module. That is exactly what we are doing byspinning the Repository Management chapter into RepositoryManagement with Nexus. We're "refactoring" the book into two.

449

Page 469: Maven Definitive Guide De

Consider this editor's note to be a deprecation warning. Eventually, we'regoing to remove this marker chapter from the book entirely. On the webit will be a HTTP redirect to the new book, and in the PDF it might be aplace-holder page that references the Nexus book.

Repository Management with Nexus

450

Page 470: Maven Definitive Guide De

Chapter 17. Writing Plugins

17.1. IntroductionWhile this chapter covers an advanced topic, don't let the idea of writing a Mavenplugin intimidate. For all of the theory and complexity of this tool, the fundamentalconcepts are easy to understand and the mechanics of writing a plugin arestraightforward. After you read this chapter, you will have a better grasp of what isinvolved in creating a Maven plugin.

17.2. Programming MavenMost of this book has dealt with using Maven, and for a book on Maven, youhaven't seen too many code examples dealing with Maven customization. In fact,you haven't yet seen any. This is by design, 99 out of 100 Maven users will neverneed to write a custom plugin to customize Maven; there is an abundance ofconfigurable plugins, and unless your project has particularly unique requirements,you will have to work to find a reason to write a new plugin. An even smallerpercentage of people who end up writing custom plugins will ever need to crackopen the source code for Maven and customize a core Maven component. If youreally need to customize the behavior of Maven, then you would write a plugin.Modifying the core Maven code is as far out of scope for most developers asmodifying the TCP/IP stack on an operating system, it is that abstract for mostMaven users.

On the other hand, if you are going to start writing a custom plugin, you are goingto have to learn a bit about the internals of Maven: How does it manage softwarecomponents? What is a Plugin? How can I customize the lifecycle? This sectionanswers some of those questions, and it introduces a few concepts at the core ofMaven's design. Learning how to write a custom Maven plugin is the gateway tocustomizing Maven itself. If you were wondering how to start understanding thecode behind Maven, you've found the proper starting line.

451

Page 471: Maven Definitive Guide De

17.2.1. What is Inversion of Control?At the heart of Maven is an Inversion of Control (IoC) container named Plexus.What does it do? It is a system for managing and relating components. While thereis a canonical essay about IoC written by Martin Fowler, the concept and termhave been so heavily overloaded in the past few years it is tough to find a gooddefinition of the concept that isn't a self-reference (or just a lazy reference to theaforementioned essay). Instead of resorting to a Wikipedia quote, we'll summarizeInversion of Control and Dependency Injection with an analogy.

Assume that you have a series of components which need to be wired together.When you think about components, think stereo components not softwarecomponents. Imagine several stereo components hooked up to a Playstation 3 and aTivo that have to interface with both an Apple TV box and a 50" flat panel LCDTV. You bring everything home from the electronics store and you purchase aseries of cables that you are going to use to connect everything to everything else.You unpack all of these components, put them in the right place and then get to thejob of hooking up fifty thousand coaxial cables and stereo jacks to fifty thousanddigital inputs and network cables. Step back from your home entertainment centerand turn on the TV, you've just performed dependency injection, and you've justbeen an inversion of control container.

So what did that have to do with anything? Your Playstation 3 and a Java Beanboth provide an interface. The Playstation 3 has two inputs: power and network,and one output to the TV. Your JavaBean has three properties: power, network,and tvOutput. When you open the box of your Playstation 3, it didn't provide youwith detailed pictures and instructions for how to connect it to every different kindof TV that might be in every different kind of house, and when you look at yourJava Bean it just provides a set of properties, not an explicit recipe for creating andmanaging an entire system of components. In an IoC container like Plexus, you areresponsible for declaring the relationships between a set of components whichsimply provide an interface of inputs and outputs. You don't instantiate objects,Plexus does; your application's code isn't responsible for managing the state ofcomponents, Plexus is. Even though it sounds very cheesy, when you start upMaven, it is starting Plexus and managing a system of related components just like

Writing Plugins

452

Page 472: Maven Definitive Guide De

your stereo system.

What are the advantages of using an IoC container? What is the advantage ofbuying discrete stereo components? If one component breaks, you can drop in areplacement for your Playstation 3 without having to spend $20,000 on the entiresystem. If you are unhappy with your TV, you can swap it out without affectingyour CD player. Most important to you, your stereo components cost less and aremore capable and reliable because manufacturers can build to a set of knowninputs and outputs and focus on building individual components. Inversion ofControl containers and Dependency Injection encourage Disaggregation and theemergence of standards. The software industry likes to imagine itself as the font ofall new ideas, but dependency injection and inversion of control are really just newwords for the concepts of Disaggregation and interchangeable machinery. If youreally want to know about DI and IoC, learn about the Model T, the Cotton Gin,and the emergence of a railroad standard in the late 19th century.

17.2.2. Introduction to PlexusThe most important feature of an IoC container implemented in Java is amechanism called dependency injection. The basic idea of IoC is that the control ofcreating and managing objects is removed from the code itself and placed into thehands of an IoC framework. Using dependency injection in an application that hasbeen programmed to interfaces, you can create components which are not bound tospecific implementations of these interfaces. Instead, you program to interfacesand then configure Plexus to connect the appropriate implementation to theappropriate component. While your code deals with interfaces, you can capture thedependencies between classes and components in an XML file that definescomponents, implementation classes, and the relationships between yourcomponents. In other words, you can write isolated components, then you can wirethem together using an XML file that defines how the components are wiredtogether. In the case of Plexus, system components are defined with an XMLdocument that is found in META-INF/plexus/components.xml.

In a Java IoC container, there are several methods for injecting dependenciesvalues into a component object: constructor, setter, or field injections. Although

Writing Plugins

453

Page 473: Maven Definitive Guide De

Plexus is capable of all three dependency injection techniques, Maven only usestwo types: field and setter injection.

Constructor InjectionConstructor injection is populating an object's values through its constructorwhen an instance of the object is created. For example, if you had an object oftype Person which had a constructor Person(String name, Job job), youcould pass in values for both name and the job via this constructor.

Setter InjectionSetter injection is using the setter method of a property on a Java bean topopulate object dependencies. For example, if you were working with a Person

object with the properties name and job, an IoC container which uses setterinjection, would create an instance of Person using a no-arg constructor. Onceit had an instance of Person, it would proceed to call the setName() andsetJob() methods.

Field InjectionBoth Constructor and Setter injection rely on a call to a public method. UsingField injection, an IoC container populates a component's dependencies bysetting an object's fields directly. For example, if you were working with aPerson object that had two fields name and job, your IoC container wouldpopulate these dependencies by setting these fields directly (i.e. person.name =

"Thomas"; person.job = job;)

17.2.3. Why Plexus?Spring does happen to be the most popular IoC container at the moment, andthere's a good argument to be made that it has affected the Java "ecosystem" for thebetter forcing companies like Sun Microsystems to yield more control to the opensource community and helping to open up standards by providing a pluggable,component-oriented "bus". But, Spring isn't the only IoC container in open source.There are many IoC containers (like PicoContainer).

Years and years ago, when Maven was created, Spring wasn't a mature option. The

Writing Plugins

454

Page 474: Maven Definitive Guide De

2"mojo." The American Heritage® Dictionary of the English Language, Fourth Edition. HoughtonMifflin Company, 2004. Answers.com 02 Mar. 2008. http://www.answers.com/topic/mojo-1

initial team of committers on Maven were more familiar with Plexus because theyinvented it, so they decided to use it as an IoC container. While it might not be aspopular as the Spring Framework, it is no less capable. And, the fact that it wascreated by the same person who created Maven makes it a perfect fit. After readingthis chapter you've have an idea of how Plexus works. If you already use an IoCcontainer you'll notice similarities and differences between Plexus and thecontainer you currently use.

NoteJust because Maven is based on Plexus doesn't mean that the Mavencommunity is "anti-Spring" (we've included a whole chapter with aSpring example in this book, portions of the Spring project are moving toMaven as a build platform). The question, "Why didn't you use Spring?"comes up often enough it did make sense to address it here. We know it,Spring is a rock star, we don't deny it, and it is on our continuing to-dolist to introduce people to (and document) Plexus: choice in the softwareindustry is always a good thing.

17.2.4. What is a Plugin?A Maven Plugin is a Maven artifact which contains a plugin descriptor and one ormore Mojos. A Mojo can be thought of as a goal in Maven, and every goalcorresponds to a Mojo. The compiler:compile goal corresponds to theCompilerMojo class in the Maven Compiler Plugin, and the jar:jar goalcorresponds to the JarMojo class in the Maven Jar Plugin. When you write yourown plugin, you are simply grouping together a set of related Mojos (or goals) in asingle plugin artifact.2

NoteMojo? What is a Mojo? The word mojo2 is defined as "a magic charm or

Writing Plugins

455

Page 475: Maven Definitive Guide De

spell", "an amulet, often in a small flannel bag containing one or moremagic items", and "personal magnetism; charm". Maven uses the termMojo because it is a play on the word Pojo (Plain-old Java Object).

A Mojo is much more than just a goal in Maven, it is a component managed byPlexus that can include references to other Plexus components.

17.3. Plugin DescriptorA Maven plugin contains a road-map for Maven that tells Maven about the variousMojos and plugin configuration. This plugin descriptor is present in the plugin JARfile in META-INF/maven/plugin.xml. When Maven loads a plugin, it reads thisXML file, instantiates and configures plugin objects to make the Mojos containedin a plugin available to Maven.

When you are writing custom Maven plugins, you will almost never need to thinkabout writing a plugin descriptor. In Chapter 10, Der Build Lebenszyklus, thelifecycle goals bound to the maven-plugin packaging type show that theplugin:descriptor goal is bound to the generate-resources phase. This goalgenerates a plugin descriptor off of the annotations present in a plugin's sourcecode. Later in this chapter, you will see how Mojos are annotated, and you willalso see how the values in these annotations end up in theMETA-INF/maven/plugin.xml file.

Example 17.1, “Plugin Descriptor” shows a plugin descriptor for the Maven ZipPlugin. This plugin is a contrived plugin that simply zips up the output directoryand produces an archive. Normally, you wouldn't need to write a custom plugin tocreate an archive from Maven, you could simply use the Maven Assembly Pluginwhich is capable of producing a distribution archive in multiple formats. Readthrough the following plugin descriptor to get an idea of the content it contains.

Example 17.1. Plugin Descriptor

<plugin><description></description>

Writing Plugins

456

Page 476: Maven Definitive Guide De

<groupId>com.training.plugins</groupId><artifactId>maven-zip-plugin</artifactId><version>1-SNAPSHOT</version><goalPrefix>zip</goalPrefix><isolatedRealm>false</isolatedRealm><inheritedByDefault>true</inheritedByDefault><mojos>

<mojo><goal>zip</goal><description>Zips up the output directory.</description><requiresDirectInvocation>false</requiresDirectInvocation><requiresProject>true</requiresProject><requiresReports>false</requiresReports><aggregator>false</aggregator><requiresOnline>false</requiresOnline><inheritedByDefault>true</inheritedByDefault><phase>package</phase><implementation>com.training.plugins.ZipMojo</implementation><language>java</language><instantiationStrategy>per-lookup</instantiationStrategy><executionStrategy>once-per-session</executionStrategy><parameters>

<parameter><name>baseDirectory</name><type>java.io.File</type><required>false</required><editable>true</editable><description>Base directory of the project.</description>

</parameter><parameter>

<name>buildDirectory</name><type>java.io.File</type><required>false</required><editable>true</editable><description>Directory containing the build files.</description>

</parameter></parameters><configuration>

<buildDirectory implementation="java.io.File">${project.build.directory}</buildDirectory>

<baseDirectory implementation="java.io.File">${basedir}</baseDirectory>

</configuration><requirements>

<requirement><role>org.codehaus.plexus.archiver.Archiver</role><role-hint>zip</role-hint><field-name>zipArchiver</field-name>

</requirement></requirements>

</mojo></mojos><dependencies>

Writing Plugins

457

Page 477: Maven Definitive Guide De

<groupId>org.apache.commons</groupId><artifactId>commons-io</artifactId><version>1.3.2</version>

</dependencies></plugin>

There are three parts to a plugin descriptor: the top-level configuration of theplugin which contains elements like groupId and artifactId, the declaration ofmojos, and the declaration of dependencies. Let's examine each of these sections inmore detail.

17.3.1. Top-level Plugin Descriptor ElementsThe top-level configuration values in the plugin element are:

descriptionThis element contains a short description of the plugin. In the case of the Zipplugin, this description is empty.

groupId, artifactId, versionJust like everything else in Maven, plugins need to have a unique coordinate.The groupId, artifactId, and version are used to locate the plugin artifact in aMaven repository.

goalPrefixThis element controls the prefix used to reference goals in a particular plugin. Ifyou were to look at the Compiler plugin's descriptor you would see thatgoalPrefix has a value of compile, and if you look at the descriptor for the Jarplugin, it would have a goalPrefix of jar. It is important that you choose adistinct goal prefix for your custom plugin.

isolatedRealm (deprecated)This is a legacy property which is no longer used by Maven. It is still present inthe system to provide for backwards compatibility with older plugins. Earlierversions of Maven used to provide a mechanism to load a plugin's dependencies

Writing Plugins

458

Page 478: Maven Definitive Guide De

in an isolated ClassLoader. Maven makes extensive use of a project calledClassWorlds from the Codehaus community to create hierarchies ofClassLoader objects which are modeled by a ClassRealm object. Feel free toignore this property and always set it to false.

inheritedByDefaultIf inheritedByDefault is set to true, any mojo in this plugin which is configuredin a parent project will be configured in a child project. If you configure a mojoto execute during a specific phase in a parent project and the Plugin hasinheritedByDefault set to true, this execution will be inherited by the childproject. If inheritedByDefault is not set to true, then an goal execution definedin a parent project will not be inherited by a child project.

17.3.2. Mojo ConfigurationNext is the declaration of the each Mojo. The plugin element contains an elementnamed mojos which contains a mojo element for each mojo present in the Plugin.Each mojo element contains the following configuration elements:

goalThis is the name of the goal. If you were running the compiler:compile goal,then compiler is the plugin's goalPrefix and compile would be the name ofthe goal.

descriptionThis contains a short description of the goal to display to the use when they usethe Help plugin to generate plugin documentation.

requiresDirectInvocationIf you set this to true, the goal can only be executed if it is explicitly executedfrom the command-line by the user. If someone tries to bind this goal to alifecycle phase in a POM, Maven will print an error message. The default forthis element is false.

requiresProject

Writing Plugins

459

Page 479: Maven Definitive Guide De

Specifies that a given goal cannot be executed outside of a project. The goalrequires a project with a POM. The default value for this requiresProject istrue.

requiresReportsIf you were creating a plugin that relies on the presence of reports, you wouldneed to set requiresReports to true. For example, if you were writing aplugin to aggregate information from a number of reports, you would setrequiresReports to true. The default for this element is false.

aggregatorA Mojo descriptor with aggregator set to true is supposed to only run onceduring the execution of Maven, it was created to give plugin developers theability to summarize the output of a series of builds; for example, to create aplugin that summarizes a report across all projects included in a build. A goalwith aggregator set to true should only be run against the top-level project ina Maven build. The default value of aggregator is false. Aggregator is slatedfor deprecation in a future release of Maven.

requiresOnlineSpecifies that a given goal cannot be executed if Maven is running in offlinemode (-o command-line option). If a goal depends on a network resource, youwould specify a value of true for this element and Maven would print an errorif the goal was executed in offline mode. The default for requiresOnline isfalse.

inheritedByDefaultIf inheritedByDefault is set to true, a mojo which is configured in a parentproject will be configured in a child project. If you configure a mojo to executeduring a specific phase in a parent project and the Mojo descriptor hasinheritedByDefault set to true, this execution will be inherited by the childproject. If inheritedByDefault is not set to true, then a goal execution definedin a parent project will not be inherited by a child project.

phase

Writing Plugins

460

Page 480: Maven Definitive Guide De

If you don't bind this goal to a specific phase, this element defines the defaultphase for this mojo. If you do not specify a phase element, Maven will requirethe user to explicitly specify a phase in a POM.

implementationThis element tells Maven which class to instantiate for this Mojo. This is aPlexus component property (defined in Plexus ComponentDescriptor).

languageThe default language for a Maven Mojo is java. This controls the PlexusComponentFactory used to create instances of this Mojo component. Thischapter focuses on writing Maven plugins in Java, but you can also writeMaven in a number of alternative languages such as Groovy, Beanshell, andRuby. If you were writing a plugin in one of these languages you would use alanguage element value other than java.

instantiationStrategyThis property is a Plexus component configuration property, it tells Plexus howto create and manage instances of the component. In Maven, all mojos aregoing to be configured with an instantiationStrategy of per-lookup; a newinstance of the component (mojo) is created every time it is retrieved fromPlexus.

executionStrategyThe execution strategy tells Maven when and how to execute a Mojo. The validvalues are once-per-session and always. Honestly, the valid values areanything, this particular property doesn't do a thing, it is a hold over from anearly design of Maven. This property is slated for deprecation in a future releaseof Maven.

parametersThis element describes all of the parameters for this Mojo. What's the name ofthe parameter What is the type of parameter? Is it required? Each parameter hasthe following elements:

Writing Plugins

461

Page 481: Maven Definitive Guide De

nameIs the name of the parameter (i.e. baseDirectory)

typeThis is the type (Java class) of the parameters (i.e. java.io.File)

requiredIs the parameter required? If true, the parameter must be non-null when thegoal is executed.

editableIf a parameter is not editable (if editable is set to false), then the value of theparameter cannot be set in the POM. For example, if the plugin descriptordefines the value of buildDirectory to be ${basedir} in the descriptor, aPOM cannot override this value to be another value in a POM.

descriptionA short description to use when generating plugin documentation (using theHelp Plugin)

configurationThis element provides default values for all of the Mojo's parameters usingMaven property notation. This example provides a default value for thebaseDir Mojo parameter and the buildDirectory Mojo parameter. In theparameter element, the implementation specifies the type of the parameter (inthis case java.io.File), the value in the parameter element contains either ahard-coded default or a Maven property reference.

requirementsThis is where the descriptor gets interesting. A Mojo is a component that ismanaged by Plexus, and, because of this, it has the opportunity to referenceother components managed by Plexus. This element allows you to definedependencies on other components in Plexus.

While you should know how to read a Plugin Descriptor, you will almost never

Writing Plugins

462

Page 482: Maven Definitive Guide De

need to write one of these descriptor files by hand. Plugin Descriptor files aregenerated automatically off of a set of annotations in the source for a Mojo.

17.3.3. Plugin DependenciesLastly, the plugin descriptor declares a set of dependencies just like a Mavenproject. When Maven uses a plugin, it will download any required dependenciesbefore it attempts to execute a goal from this plugin. In this example, the plugindepends on Jakarta Commons IO version 1.3.2.

17.4. Writing a Custom PluginWhen you write a custom plugin, you are going to be writing a series of Mojos(goals). Every Mojo is a single Java class which contains a series of annotationsthat tell Maven how to generate the Plugin descriptor described in the previoussection. Before you can start writing Mojo classes, you will need to create Mavenproject with the appropriate packaging and POM.

17.4.1. Creating a Plugin ProjectTo create a plugin project, you should use the Maven Archetype plugin. Thefollowing command-line will create a plugin with a groupId oforg.sonatype.mavenbook.plugins and the artifactId of first-maven-plugin:

$ mvn archetype:create \-DgroupId=org.sonatype.mavenbook.plugins \-DartifactId=first-maven-plugin \-DarchetypeGroupId=org.apache.maven.archetypes \-DarchetypeArtifactId=maven-archetype-mojo

The Archetype plugin is going to create a directory named my-first-plugin whichcontains the following POM.

Example 17.2. A Plugin Project's POM

<?xml version="1.0" encoding="UTF-8"?><project><modelVersion>4.0.0</modelVersion>

Writing Plugins

463

Page 483: Maven Definitive Guide De

<groupId>org.sonatype.mavenbook.plugins</groupId><artifactId>first-maven-plugin</artifactId><version>1.0-SNAPSHOT</version><packaging>maven-plugin</packaging><name>first-maven-plugin Maven Mojo</name><url>http://maven.apache.org</url><dependencies>

<dependency><groupId>org.apache.maven</groupId><artifactId>maven-plugin-api</artifactId><version>2.0</version>

</dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>3.8.1</version><scope>test</scope>

</dependency></dependencies>

</project>

The most import element in a plugin project's POM is the packaging elementwhich has a value of maven-plugin. This packaging element customizes theMaven lifecycle to include the necessary goals to create a plugin descriptor. Theplugin lifecycle was introduce in Section 10.2.3, “ plugin”, it is similar to the Jarlifecycle with three exceptions: plugin:descriptor is bound to thegenerate-resources phase, plugin:addPluginArtifactMetadata is added to thepackage phase, and plugin:updateRegistry is added to the install phase.

The other important piece of a plugin project's POM is the dependency on theMaven Plugin API. This project depends on version 2.0 of the maven-plugin-api

and it also adds in JUnit as a test-scoped dependency.

17.4.2. A Simple Java MojoIn this chapter, we're going to introduce a Maven Mojo written in Java. Each Mojoin your project is going to implement the org.apache.maven.plugin.Mojo

interface, the Mojo implementation shown in the following example implementsthe Mojo interface by extending the org.apache.maven.plugin.AbstractMojo

class. Before we dive into the code for this Mojo, let's take some time to explorethe methods on the Mojo interface. Mojo provides the following methods:

Writing Plugins

464

Page 484: Maven Definitive Guide De

void setLog( org.apache.maven.monitor.logging.Log log )

Every Mojo implementation has to provide a way for the plugin to communicatethe progress of a particular goal. Did the goal succeed? Or, was there a problemduring goal execution? When Maven loads and executes a Mojo, it is going tocall the setLog() method and supply the Mojo instance with a suitable loggingdestination to be used in your custom plugin.

protected Log getLog()

Maven is going to call setLog() before your Mojo is executed, and your Mojocan retrieve the logging object by calling getLog(). Instead of printing outstatus to Standard Output or the console, your Mojo is going to invoke methodson the Log object.

void execute() throws

org.apache.maven.plugin.MojoExecutionException

This method is called by Maven when it is time to execute your goal.The Mojo interface is concerned with two things: logging the results of goalexecution and executing a goal. When you are writing a custom plugin, you'll beextending AbstractMojo. AbstractMojo takes care of handling the setLog() andgetLog() implementations and contains an abstract execute() method. When youextend AbstractMojo, all you need to do is implement the execute() method.Example 17.3, “A Simple EchoMojo” shows a trivial Mojo implement whichsimply prints out a message to the console.

Example 17.3. A Simple EchoMojo

package org.sonatype.mavenbook.plugins;

import org.apache.maven.plugin.AbstractMojo;import org.apache.maven.plugin.MojoExecutionException;import org.apache.maven.plugin.MojoFailureException;

/*** Echos an object string to the output screen.* @goal echo* @requiresProject false*/public class EchoMojo extends AbstractMojo{

Writing Plugins

465

Page 485: Maven Definitive Guide De

/*** Any Object to print out.* @parameter expression="${echo.message}" default-value="Hello World..."*/private Object message;

public void execute()throws MojoExecutionException, MojoFailureException

{getLog().info( message.toString() );

}}

If you create this Mojo in ${basedir} under src/main/java inorg/sonatype/mavenbook/mojo/EchoMojo.java in the project created in theprevious section and run mvn install, you should be able to invoke this goaldirectly from the command-line with:

$ mvn org.sonatype.mavenbook.plugins:first-maven-plugin:1.0-SNAPSHOT:echo

That large command-line is mvn followed by thegroupId:artifactId:version:goal. When you run this command-line youshould see output that contains the output of the echo goal with the defaultmessage: "Hello Maven World...". If you want to customize the message, you canpass the value of the message parameter with the following command-line:

$ mvn org.sonatype.mavenbook.plugins:first-maven-plugin:1.0-SNAPSHOT:echo \-Decho.message="The Eagle has Landed"

The previous command-line is going to execute the EchoMojo and print out themessage "The Eagle has Landed".

17.4.3. Configuring a Plugin PrefixSpecifying the groupId, artifactId, version, and goal on the command-line iscumbersome. To address this, Maven assigns a plugin a prefix. Instead of typing:

$ mvn org.apache.maven.plugins:maven-jar-plugin:2.2:jar

You can use the plugin prefix jar and turn that command-line into mvn jar:jar.

Writing Plugins

466

Page 486: Maven Definitive Guide De

How does Maven resolve something like jar:jar toorg.apache.mven.plugins:maven-jar:2.3? Maven looks at a file in the Mavenrepository to obtain a list of plugins for a specific groupId. By default, Maven isconfigured to look for plugins in two groups: org.apache.maven.plugins andorg.codehaus.mojo. When you specify a new plugin prefix like mvnhibernate3:hbm2ddl, Maven is going to scan the repository metadata for theappropriate plugin prefix. First, Maven is going to scan theorg.apache.maven.plugins group for the plugin prefix hibernate3. If it doesn'tfind the plugin prefix hibernate3 in the org.apache.maven.plugins group it willscan the metadata for the org.codehaus.mojo group.

When Maven scans the metadata for a particular groupId, it is retrieving an XMLfile from the Maven repository which captures metadata about the artifactscontained in a group. This XML file is specific for each repository referenced, ifyou are not using a custom Maven repository, you will be able to see the Mavenmetadata for the org.apache.maven.plugins group in your local Mavenrepository (~/.m2/repository) underorg/apache/maven/plugins/maven-metadata-central.xml. Example 17.4,“Maven Metadata for the Maven Plugin Group” shows a snippet of themaven-metadata-central.xml file from the org.apache.maven.plugin group.

Example 17.4. Maven Metadata for the Maven Plugin Group

<?xml version="1.0" encoding="UTF-8"?><metadata>

<plugins><plugin><name>Maven Clean Plugin</name><prefix>clean</prefix><artifactId>maven-clean-plugin</artifactId>

</plugin><plugin><name>Maven Compiler Plugin</name><prefix>compiler</prefix><artifactId>maven-compiler-plugin</artifactId>

</plugin><plugin><name>Maven Surefire Plugin</name><prefix>surefire</prefix><artifactId>maven-surefire-plugin</artifactId>

</plugin>...

Writing Plugins

467

Page 487: Maven Definitive Guide De

</plugins></metadata>

As you can see in Example 17.4, “Maven Metadata for the Maven Plugin Group”,this maven-metadata-central.xml file in your local repository is what makes itpossible for your to execute mvn surefire:test. Maven scansorg.apache.maven.plugins and org.codehaus.mojo: plugins fromorg.apache.maven.plugins are considered core Maven plugins and plugins fromorg.codehaus.mojo are considered extra plugins. The Apache Maven projectmanages the org.apache.maven.plugins group, and a separate independent opensource community manages the Codehaus Mojo project. If you would like to startpublishing plugins to your own groupId, and you would like Maven toautomatically scan your own groupId for plugin prefixes, you can customize thegroups that Maven scans for plugins in your Maven Settings.

If you wanted to be able to run the first-maven-plugin's echo goal by runningfirst:echo, add the org.sonatype.mavenbook.plugins groupId to your~/.m2/settings.xml as shown in Example 17.5, “Customizing the Plugin Groupsin Maven Settings”. This will prepend the org.sonatype.mavenbook.plugins tothe list of groups which Maven scans for Maven plugins.

Example 17.5. Customizing the Plugin Groups in Maven Settings

<settings>...<pluginGroups>

<pluginGroup>org.sonatype.mavenbook.plugins</pluginGroup></pluginGroups>

</settings>

You can now run mvn first:echo from any directory and see that Maven willproperly resolve the goal prefix to the appropriate plugin identifiers. This workedbecause our project adhered to a naming convention for Maven plugins. If yourplugin project has an artifactId which follows the pattern maven-first-plugin

or first-maven-plugin. Maven will automatically assign a plugin goal prefix offirst to your plugin. In other words, when the Maven Plugin Plugin is generating

Writing Plugins

468

Page 488: Maven Definitive Guide De

the Plugin descriptor for your plugin and you have not explicitly set thegoalPrefix in your project, the plugin:descriptor goal will extract the prefixfrom your plugin's artifactId when it matches the following patterns:

• ${prefix}-maven-plugin, OR

• maven-${prefix}-plugin

If you would like to set an explicit plugin prefix, you'll need to configure theMaven Plugin Plugin. The Maven Plugin Plugin is a plugin that is responsible forbuilding the Plugin descriptor and performing plugin specific tasks during thepackage and load phases. The Maven Plugin Plugin can be configured just like anyother plugin in the build element. To set the plugin prefix for your plugin, add thefollowing build element to the first-maven-plugin project's pom.xml.

Example 17.6. Configuring a Plugin Prefix

<?xml version="1.0" encoding="UTF-8"?><project><modelVersion>4.0.0</modelVersion><groupId>org.sonatype.mavenbook.plugins</groupId><artifactId>first-maven-plugin</artifactId><version>1.0-SNAPSHOT</version><packaging>maven-plugin</packaging><name>first-maven-plugin Maven Mojo</name><url>http://maven.apache.org</url><build>

<plugins><plugin>

<artifactId>maven-plugin-plugin</artifactId><version>2.3</version><configuration>

<goalPrefix>blah</goalPrefix></configuration>

</plugin></plugins>

</build><dependencies>

<dependency><groupId>org.apache.maven</groupId><artifactId>maven-plugin-api</artifactId><version>2.0</version>

</dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>3.8.1</version>

Writing Plugins

469

Page 489: Maven Definitive Guide De

<scope>test</scope></dependency>

</dependencies></project>

Example 17.6, “Configuring a Plugin Prefix” sets the plugin prefix to blah. Ifyou've added the org.sonatype.mavenbook.plugins to the pluginGroups in your~/.m2/settings.xml, you should be able to execute the EchoMojo by running mvnecho:blah from any directory.

17.4.4. Logging from a PluginMaven takes care of connecting your Mojo to a logging provider by callingsetLog() prior to the execution of your Mojo. It supplies an implementation oforg.apache.maven.monitor.logging.Log. This class exposes methods that youcan use to communicate information back to the user. This Log class providesmultiple levels of logging similar to that API provided by Log4J. Those levels arecaptured by a series of methods available for each level: debug, info, error andwarn. To save trees, we've only listed the methods for a single logging level:debug.

void debug( CharSequence message )

Prints a message to the debug logging level.

void debug( CharSequence message, Throwable t )

Prints a message to the debug logging level which includes the stack trace fromthe Throwable (either Exception or Error)

void debug( Throwable t )

Prints out the stack trace of the Throwable (either Exception or Error)Each of the four levels exposes the same three methods. The four logging levelsserve different purposes. The debug level exists for debugging purposes and forpeople who want to see a very detailed picture of the execution of a Mojo. Youshould use the debug logging level to provide as much detail on the execution of aMojo, but you should never assume that a user is going to see the debug level. The

Writing Plugins

470

Page 490: Maven Definitive Guide De

info level is for general informational messages that should be printed as a normalcourse of operation. If you were building a plugin that compiled code using acompiler, you might want to print the output of the compiler to the screen.

The warn logging level is used for messages about unexpected events and errorsthat your Mojo can cope with. If you were trying to run a plugin that compiledRuby source code, and there was no Ruby source code available, you might wantto just print a warning message and move on. Warnings are not fatal, but errors areusually build-stopping conditions. For the completely unexpected error condition,there is the error logging level. You would use error if you couldn't continueexecuting a Mojo. If you were writing a Mojo to compile some Java code and thecompiler wasn't available, you'd print a message to the error level and possiblypass along an Exception that Maven could print out for the user. You shouldassume that a user is going to see most of the messages in info and all of themessages in error.

17.4.5. Mojo Class AnnotationsIn first-maven-plugin, you didn't write the plugin descriptor yourself, you reliedon Maven to generate the plugin descriptor from your source code. The descriptorwas generated using your plugin project's POM information and a set ofannotations on your EchoMojo class. EchoMojo only specifies the @goal annotation,here is a list of other annotations you can place on your Mojo implementation.

@goal <goalName>This is the only required annotation which gives a name to this goal unique tothis plugin.

@requiresDependencyResolution <requireScope>Flags this mojo as requiring the dependencies in the specified scope (or animplied scope) to be resolved before it can execute. Supports compile, runtime,and test. If this annotation had a value of test, it would tell Maven that theMojo cannot be executed until the dependencies in the test scope had beenresolved.

Writing Plugins

471

Page 491: Maven Definitive Guide De

@requiresProject (true|false)Marks that this goal must be run inside of a project, default is true. This isopposed to plugins like archetypes, which do not.

@requiresReports (true|false)If you were creating a plugin that relies on the presence of reports, you wouldneed to set requiresReports to true. The default value of this annotation isfalse.

@aggregator (true|false)A Mojo with aggregator set to true is supposed to only run once during theexecution of Maven, it was created to give plugin developers the ability tosummarize the output of a series of builds; for example, to create a plugin thatsummarizes a report across all projects included in a build. A goal withaggregator set to true should only be run against the top-level project in aMaven build. The default value of aggregator is false.

@requiresOnline (true|false)When set to true, Maven must not be running in offline mode when this goal isexecuted. Maven will throw an error if one attempts to execute this goal offline.Default: false.

@requiresDirectInvocationWhen set to true, the goal can only be executed if it is explicitly executed fromthe command-line by the user. Maven will throw an error if someone tries tobind this goal to a lifecycle phase. The default for this annotation is false.

@phase <phaseName>This annotation specifies the default phase for this goal. If you add an executionfor this goal to a pom.xml and do not specify the phase, Maven will bind thegoal to the phase specified in this annotation by default.

@execute [goal=goalName|phase=phaseName [lifecycle=lifecycleId]]This annotation can be used in a number of ways. If a phase is supplied, Maven

Writing Plugins

472

Page 492: Maven Definitive Guide De

will execute a parallel lifecycle ending in the specified phase. The results of thisseparate execution will be made available in the Maven property${executedProperty}.The second way of using this annotation is to specify an explicit goal using theprefix:goal notation. When you specify just a goal, Maven will execute thisgoal in a parallel environment that will not affect the current Maven build.

The third way of using this annotation would be to specify a phase in analternate lifecycle using the identifier of a lifecycle.

@execute phase="package" lifecycle="zip"@execute phase="compile"@execute goal="zip:zip"

If you look at the source for EchoMojo, you'll notice that Maven is not using thestandard annotations available in Java 5. Instead, it is using Commons Attributes.Commons Attributes provided a way for Java programmers to use annotationsbefore annotations were a part of the Java language specification. Why doesn'tMaven use Java 5 annotations? Maven doesn't use Java 5 annotations because it isdesigned to target pre-Java 5 JVMs. Because Maven has to support older versionsof Java, it cannot use any of the newer features available in Java 5.

17.4.6. When a Mojo FailsThe execute() method in Mojo throws two exceptions MojoExecutionExceptionand MojoFailureException. The difference between these two exception is bothsubtle and important, and it relates to what happens when a goal execution "fails".A MojoExecutionException is a fatal exception, something unrecoverablehappened. You would throw a MojoExecutionException if something happensthat warrants a complete stop in a build; you re trying to write to disk, but there isno space left, or you were trying to publish to a remote repository, but you can'tconnect to it. Throw a MojoExecutionException if there is no chance of a buildcontinuing; something terrible has happened and you want the build to stop and theuser to see a "BUILD ERROR" message.

A MojoFailureException is something less catastrophic, a goal can fail, but it

Writing Plugins

473

Page 493: Maven Definitive Guide De

might not be the end of the world for your Maven build. A unit test can fail, or aMD5 checksum can fail; both of these are potential problems, but you don't want toreturn an exception that is going to kill the entire build. In this situation you wouldthrow a MojoFailureException. Maven provides for different "resiliency" settingswhen it comes to project failure. Which are described below.

When you run a Maven build, it could involve a series of projects each of whichcan succeed or fail. You have the option of running Maven in three failure modes:

mvn -ffFail-fast mode: Maven will fail (stop) at the first build failure.

mvn -faeFail-at-end: Maven will fail at the end of the build. If a project in the Mavenreactor fails, Maven will continue to build the rest of the builds and report afailure at the end of the build.

mvn -fnFail never: Maven won't stop for a failure and it won't report a failure.

You might want to ignore failure if you are running a continuous integration buildand you want to attempt a build regardless of the success of failure of an individualproject build. As a plugin developer, you'll have to make a call as to whether aparticular failure condition is a MojoExecutionException or aMojoFailureExeception.

17.5. Mojo ParametersJust as important as the execute() method and the Mojo annotations, a Mojo isconfigured via parameters. This section deals with some configuration and topicssurrounding Mojo parameters.

17.5.1. Supplying Values for Mojo ParametersIn EchoMojo we declared the message parameter with the following annotations:

Writing Plugins

474

Page 494: Maven Definitive Guide De

/*** Any Object to print out.* @parameter* expression="${echo.message}"* default-value="Hello Maven World"*/private Object message;

The default expression for this parameter is ${echo.message}, this means thatMaven will try to use the value of the echo.message property to set the value formessage. If the value of the echo.message property is null, the default-valueattribute of the @parameter annotation will be used instead. Instead of using theecho.message property, we can configure a value for the message parameter of theEchoMojo directly in a project's POM.

There are a few ways to populate the message parameter in the EchoMojo. First wecan pass in a value from the command-line like this (assuming that you've addedorg.sonatype.mavenbook.plugins to your pluginGroups):

$ mvn first:echo -Decho.message="Hello Everybody"

We could also specify the value of this message parameter, by setting a property inour POM or in our settings.xml.

<project>...<properties>

<echo.message>Hello Everybody</echo.message></properties>

</project>

This parameter could also be configured directly as a configuration value for theplugin. If we wanted to customize the message parameter directly, we could usethe following build configuration. The following configuration bypasses theecho.message property and populates the Mojo parameter in plugin configuration.

<project>...<build>

<plugins><plugin>

<groupId>org.sonatype.mavenbook.plugins</groupId><artifactId>first-maven-plugin</artifactId><version>1.0-SNAPSHOT</version>

Writing Plugins

475

Page 495: Maven Definitive Guide De

<configuration><message>Hello Everybody!</message>

</configuration></plugin>

</plugins></build>

</project>

If we wanted to run the EchoMojo twice at difference phases in a lifecycle, and wewanted to customize the message parameter for each execution separately, wecould configure the parameter value at the execution level in a POM like this:

<build><build>

<plugins><plugin>

<groupId>org.sonatype.mavenbook.plugins</groupId><artifactId>first-maven-plugin</artifactId><version>1.0-SNAPSHOT</version><executions>

<execution><id>first-execution</id><phase>generate-resources</phase><goals>

<goal>echo</goal></goals><configuration>

<message>The Eagle has Landed!</message></configuration>

</execution><execution><id>second-execution</id><phase>validate</phase><goals>

<goal>echo</goal></goals><configuration>

<message>${project.version}</message></configuration>

</execution></executions>

</plugin></plugins>

</build></build>

While this last configuration example seems very verbose, it illustrates theflexibility of Maven. In the previous configuration example, you've bound theEchoMojo to both the validate and generate-resources phases in the default

Writing Plugins

476

Page 496: Maven Definitive Guide De

Maven lifecycle. The first execution is bound to generate-resources, it supplies astring value to the message parameter of "The Eagle has Landed!". The secondexecution is bound to the validate phase, it supplies a property reference to${project.version}. When you run mvn install for his project, you'll see that thefirst:echo goal executes twice and prints out two different messages.

17.5.2. Multi-valued Mojo ParametersPlugins can have parameters which accept more than one value. Take a look at theZipMojo shown in Example 17.7, “A Plugin with Multi-valued Parameters”. Boththe includes and excludes parameters are multivalued String arrays whichspecify the inclusion and exclusion patterns for a component that creates a ZIP file.

Example 17.7. A Plugin with Multi-valued Parameters

package org.sonatype.mavenbook.plugins

/*** Zips up the output directory.* @goal zip* @phase package*/public class ZipMojo extends AbstractMojo{

/*** The Zip archiver.* @parameter \

expression="${component.org.codehaus.plexus.archiver.Archiver#zip}"*/private ZipArchiver zipArchiver;

/*** Directory containing the build files.* @parameter expression="${project.build.directory}"*/private File buildDirectory;

/*** Base directory of the project.* @parameter expression="${basedir}"*/private File baseDirectory;

/*** A set of file patterns to include in the zip.

Writing Plugins

477

Page 497: Maven Definitive Guide De

* @parameter alias="includes"*/private String[] mIncludes;

/*** A set of file patterns to exclude from the zip.* @parameter alias="excludes"*/private String[] mExcludes;

public void setExcludes( String[] excludes ) { mExcludes = excludes; }

public void setIncludes( String[] includes ) { mIncludes = includes; }

public void execute()throws MojoExecutionException

{try {

zipArchiver.addDirectory( buildDirectory, includes, excludes );zipArchiver.setDestFile( new File( baseDirectory, "output.zip" ) );zipArchiver.createArchive();

} catch( Exception e ) {throw new MojoExecutionException( "Could not zip", e );

}}

}

To configure a multi-valued Mojo parameter, you use a series of elements for eachvalue. If the name of the multi-valued parameter is includes, you would use anelement includes with child elements include. If the multi-valued parameter isexcludes, you would use an element excludes with child elements exclude. Toconfigure the ZipMojo to ignore all files ending in .txt and all files ending in atilde, you would use the following plugin configuration.

<project>...<build>

<plugins><plugin>

<groupId>org.sonatype.mavenbook.plugins</groupId><artifactId>zip-maven-plugin</artifactId><configuration>

<excludes><exclude>**/*.txt</exclude><exclude>**/*~</exclude>

</excludes></configuration>

</plugin>

Writing Plugins

478

Page 498: Maven Definitive Guide De

</plugins></build>

</project>

17.5.3. Depending on Plexus ComponentsA Mojo is a component managed by an IoC container called Plexus. A Mojo candepend on other components managed by Plexus by declaring a Mojo parameterand using the @parameter or the @component annotation. Example 17.7, “A Pluginwith Multi-valued Parameters” shows a ZipMojo which depends on a Plexuscomponent using the @parameter annotation, this dependency could be declaredusing the @component annotation.

Example 17.8. Depending on a Plexus Component

/*** The Zip archiver.* @component role="org.codehaus.plexus.archiver.Archiver" roleHint="zip"*/private ZipArchiver zipArchiver;

When Maven instantiates this Mojo, it will then attempt to retrieve the Plexuscomponent with the specified role and role hint. In this example, the Mojo will berelated to a ZipArchiver component which will allow the ZipMojo to create a ZIPfile.

17.5.4. Mojo Parameter AnnotationsUnless you insist on writing your Plugin descriptors by hand, you'll never have towrite that XML. Instead, the Maven Plugin Plugin has a plugin:descriptor goalbound to the generate-resources phase. This goal generates the plugin descriptorfrom annotations on your Mojo. To configure a Mojo parameter, you should usethe following annotations on either the private member variables for each of yourMojo's parameters. You can also use these annotations on public setter methods,but the most common convention for Maven plugins is to annotate private member

Writing Plugins

479

Page 499: Maven Definitive Guide De

variables directly.

@parameter [alias="someAlias"] [expression="${someExpression}"][default-value="value"]Marks a private field (or a setter method) as a parameter. The alias providesthe name of the parameter. If alias is omitted, Maven will use the name of thevariable as the parameter name. The expression is an expression that Mavenwill evaluate to obtain a value. Usually the expression is a property referencelike ${echo.message}. default-value is the value that this Mojo will use if novalue can be derived from the expression or if a value was not explicitlysupplied via plugin configuration in a POM.

@requiredIf this annotation is present, a valid value for this parameter is required prior toMojo execution. If Maven tries to execute this Mojo and the parameter has anull value, Maven will throw and error when it tries to execute this goal.

@readonlyIf this annotation is present, the user cannot directly configuration thisparameter in the POM. You would use this annotation with the expressionattribute of the parameter annotation. For example, if you wanted to make surethat a particular parameter always had the value of the finalName POMproperty, you would list an expression of ${build.finalName} and then add the@readOnly annotation. If this were the case, the user could only change thevalue of this parameter by changing the value of finalName in the POM.

@componentTells Maven to populate a field with a Plexus Component. A valid value for the@component annotation would be:

@component role="org.codehaus.plexus.archiver.Archiver" roleHint="zip"

This would have the effect of retrieving the ZipArchiver from Plexus. TheZipArchiver is the Archiver which corresponds to the role hint zip. Instead ofcomponent, you could also use the @parameter annotation with an expressionattribute of:

Writing Plugins

480

Page 500: Maven Definitive Guide De

@parameter expression="${component.org.codehaus.plexus.archiver.Archiver#zip}"

While the two annotations are effectively the same, the @component annotationis the preferred way to configure dependencies on Plexus components.

@deprecatedThe parameter will be deprecated. Users can continue configuring thisparameter, but a warning message will be displayed.

17.6. Plugins and the Maven LifecycleIn the Chapter 10, Der Build Lebenszyklus chapter, you learned that lifecycles canbe customized by packaging types. A plugin can both introduce a new packagingtype and customize the lifecycle. In this section, you are going to learn how youcan customize the lifecycle from a custom Maven plugin. You are also some to seehow you can tell a Mojo to execute a parallel lifecycle.

17.6.1. Executing a Parallel LifecycleLet's assume you write some goal that depends on the output from a previous build.Maybe the ZipMojo goal can only run if there is output to include in an archive.You can specify something like a prerequisite goal by using the @execute

annotation on a Mojo class. This annotation will cause Maven to spawn a parallelbuild and execute a goal or a lifecycle in a parallel instance of Maven that isn'tgoing to affect the current build. Maybe you wrote some Mojo that you can to runonce a day that runs mvn install and then packages up all of the output in somesort of customized distribution format. Your Mojo descriptor could tell Maven thatbefore you execute your CustomMojo, you'd like it to execute the default lifecycleup to the install phase and then expose the results of that project to your Mojo asthe property ${executedProject}. You could then reference properties in thatproject to before some sort of post processing.

Another possibility is that you have a goal that does something completelyunrelated to the default lifecycle. Let's consider something completely unexpected,

Writing Plugins

481

Page 501: Maven Definitive Guide De

maybe you have a goal that turns a WAV file into an MP3 using something likeLAME, but before you do that, you want to step through a lifecycle that turns aMIDI file to a WAV. (You can use Maven for anything, this isn't that "far out".)You've created a "midi-sound" lifecycle, and you want to include the output of themidi-sound lifecycle's install phase in your web application project which has awar packaging type. Since your project is running in the war packaging lifecycle,you'll need to have goal that effectively forks off an isolated build and runs throughthe midi-source lifecycle. You would do this by annotating your mojo with@execute lifecycle="midi-source" phase="install".

@execute goal="<goal>"This will execute the given goal before execution of this one. The goal name isspecified using the prefix:goal notation.

@execute phase="<phase>"This will fork an alternate build lifecycle up to the specified phase beforecontinuing to execute the current one. If no lifecycle is specified, Maven willuse the lifecycle of the current build.

@execute lifecycle="<lifecycle>" phase="<phase>"This will execute the given alternate lifecycle. A custom lifecycle can bedefined in META-INF/maven/lifecycle.xml.

17.6.2. Creating a Custom LifecycleA custom lifecycle must be packaged in the plugin under theMETA-INF/maven/lifecycle.xml file. You can include a lifecycle undersrc/main/resources in META-INF/maven/lifecycle.xml. The followinglifecycle.xml declares a lifecycle named zipcycle that contains only the zip

goal in a package phase.

Example 17.9. Define a Custom Lifecycle in lifecycle.xml

<lifecycles><lifecycle>

<id>zipcycle</id>

Writing Plugins

482

Page 502: Maven Definitive Guide De

<phases><phase>

<id>package</id><executions>

<execution><goals>

<goal>zip</goal></goals>

</execution></executions>

</phase></phases>

</lifecycle></lifecycles>

If you wanted to execute the zipcycle phase within another build, you could thencreate a ZipForkMojo which uses the @execute annotation to tell Maven to stepthrough the zipcycle phase to package when the ZipForkMojo is executed.

Example 17.10. Forking a Customer Lifecycle from a Mojo

/*** Forks a zip lifecycle.* @goal zip-fork* @execute lifecycle="zipcycle" phase="package"*/public class ZipForkMojo extends AbstractMojo{

public void execute()throws MojoExecutionException

{getLog().info( "doing nothing here" );

}}

Running the ZipForkMojo will fork the lifecycle. If you've configured your pluginto execute with the goal prefix zip, running zip-fork should produce somethingsimilar to the following output.

$ mvn zip:zip-fork[INFO] Scanning for projects...[INFO] Searching repository for plugin with prefix: 'zip'.[INFO] ----------------------------------------------------------------------[INFO] Building Maven Zip Forked Lifecycle Test

Writing Plugins

483

Page 503: Maven Definitive Guide De

[INFO] task-segment: [zip:zip-fork][INFO] ----------------------------------------------------------------------[INFO] Preparing zip:zip-fork[INFO] [site:attach-descriptor][INFO] [zip:zip][INFO] Building zip: \

~/maven-zip-plugin/src/projects/zip-lifecycle-test/target/output.zip[INFO] [zip:zip-fork][INFO] doing nothing here[INFO] ---------------------------------------------------------------------[INFO] BUILD SUCCESSFUL[INFO] ---------------------------------------------------------------------[INFO] Total time: 1 second[INFO] Finished at: Sun Apr 29 16:10:06 CDT 2007[INFO] Final Memory: 3M/7M[INFO] ---------------------------------------------------------------------

Calling zip-fork spawned another lifecycle, Maven executed the zipcycle

lifecycle then it printed out the message from ZipFormMojo's execute method.

17.6.3. Overriding the Default LifecycleOnce you've created your own lifecycle and spawned it from a Mojo. The nextquestion you might have is how do you override the default lifecycle. How do youcreate custom lifecycles and attach them to projects? In Chapter 10, Der BuildLebenszyklus, we saw that the packaging of a project defines the lifecycle of aproject. There's something different about almost every packaging type; warattached different goals to package, custom lifecycles like swf from the Israfil Flex3 plugin attach different goals to the compile phase. When you create a customlifecycle, you can attach that lifecycle to a packaging type by supplying somePlexus configuration in your plugin's archive.

To define a new lifecycle for a new packaging type, you'll need to configure aLifecycleMapping component in Plexus. In your plugin project, create aMETA-INF/plexus/components.xml under src/main/resources. In components.xmladd the content from Example 17.11, “Overriding the Default Lifecycle”. Set thename of the packaging type under role-hint, and the set of phases containing thecoordinates of the goals to bind (omit the version). Multiple goals can beassociated with a phase using a comma delimited list.

Writing Plugins

484

Page 504: Maven Definitive Guide De

Example 17.11. Overriding the Default Lifecycle

<component-set><components>

<component><role>org.apache.maven.lifecycle.mapping.LifecycleMapping</role><role-hint>zip</role-hint><implementation>

org.apache.maven.lifecycle.mapping.DefaultLifecycleMapping</implementation><configuration>

<phases><process-resources>org.apache.maven.plugins:maven-resources-plugin:resources

</process-resources><compile>org.apache.maven.plugins:maven-compiler-plugin:compile

</compile><package>org.sonatype.mavenbook.plugins:maven-zip-plugin:zip</package>

</phases></configuration>

</component></components>

</component-set>

If you create a plugin which defines a new packaging type and a customizedlifecycle, Maven won't know anything it until you add the plugin to your project'sPOM and set the extensions element to true. Once you do this, Maven will scanyour plugin for more than just Mojos to execute, it will look for thecomponents.xml under META-INF/plexus, and it will make the packaging typeavailable to your project.

Example 17.12. Configuring a Plugin as an Extension

<project>...<build>

...<plugins><plugin>

<groupId>com.training.plugins</groupId><artifactId>maven-zip-plugin</artifactId><extensions>true</extensions>

</plugin></plugins>

Writing Plugins

485

Page 505: Maven Definitive Guide De

</build></project>

Once you add the plugin with the extensions element set to true, you can use thecustom packaging type and your project will be able to execute the customlifecycle associated with that packaging type.

Writing Plugins

486

Page 506: Maven Definitive Guide De

Chapter 18. Writing Plugins in AlternativeLanguagesYou can write a Mojo in Java, or you can write a Mojo in an alternative language.Maven has support for a number of implementation languages, and this chapter isgoing to show you how to create plugins in three languages: Groovy, Ant, andRuby plugins.

18.1. Writing Plugins in AntAnt isn't a language as much as it is a build tool which allows you to describe abuild as a set of tasks grouped into build targets. Ant then allows you to declaredependencies between build targets; for example, in Ant you are essentiallycreating your own lifecycle. An Ant build.xml might have an install target whichdepends on a test target which depends on a compile target. Ant is something of aancestor to Maven, it was the ubiquitous procedural build tool that almost everyproject used before Maven introduced the concept of wide-scale reusability ofcommon build plugins and the concept of a universal lifecycle.

While Maven is an improvement on Ant, Ant can still be useful when describingparts of the build process. Ant provides a set of tasks which can come in handywhen you need to perform file operations or XSLT transformations or any otheroperation you could think of. There is a large library of available Ant tasks foreverything from running JUnit tests to transforming XML to copying files to aremote server using SCP. An overview of available Ant tasks can be found onlinein the Apache Ant Manual. You can use these tasks as a low-level buildcustomization language, and you can also write a Maven plugin where, instead of aMojo written in Java, you can pass parameters to a Mojo which is an Ant buildtarget.

18.2. Creating an Ant Plugin

487

Page 507: Maven Definitive Guide De

To create a Maven plugin using Ant, you will need to have a pom.xml and a singleMojo implemented in Ant. To get started, create a project directory namedfirstant-maven-plugin. Place the following pom.xml in this directory.

Example 18.1. POM for an Ant Maven Plugin

<project><modelVersion>4.0.0</modelVersion><groupId>org.sonatype.mavenbook.plugins</groupId><artifactId>firstant-maven-plugin</artifactId><name>Example Ant Mojo - firstant-maven-plugin</name><packaging>maven-plugin</packaging><version>1.0-SNAPSHOT</version><dependencies>

<dependency><groupId>org.apache.maven</groupId><artifactId>maven-script-ant</artifactId><version>2.0.9</version>

</dependency></dependencies><build>

<plugins><plugin>

<artifactId>maven-plugin-plugin</artifactId><version>2.4</version><dependencies>

<dependency><groupId>org.apache.maven.plugin-tools</groupId><artifactId>maven-plugin-tools-ant</artifactId><version>2.4</version>

</dependency></dependencies>

</plugin></plugins>

</build></project>

Next, you will need to create your Ant Mojo. An Ant mojo consists of two parts:the Ant tasks in an XML file, and a file which supplies Mojo descriptorinformation. The Ant plugin tools are going to look for both of these files in${basedir}/src/main/scripts. One file will be named echo.build.xml and itwill contain the Ant XML.

Example 18.2. Echo Ant Mojo

Writing Plugins in Alternative Languages

488

Page 508: Maven Definitive Guide De

<project><target name="echotarget">

<echo>${message}</echo></target>

</project>

The other file will describe the Echo Ant Mojo and will be in the echo.mojos.xml

file also in ${basedir}/src/main/scripts.

Example 18.3. Echo Ant Mojo Descriptor

<pluginMetadata><mojos>

<mojo><goal>echo</goal><call>echotarget</call><description>Echos a Message</description><parameters>

<parameter><name>message</name><property>message</property><required>false</required><expression>${message}</expression><type>java.lang.Object</type><defaultValue>Hello Maven World</defaultValue><description>Prints a message</description>

</parameter></parameters>

</mojo></mojos>

</pluginMetadata>

This echo.mojos.xml file configures the Mojo descriptor for this plugin. Itsupplies the goal name "echo", and it tells Maven what Ant task to call in the callelement. In addition to configuring the description, this XML file configures themessage parameter to use the expression ${message} and to have a default value of"Hello Maven World."

If you've configured your plugin groups in ~/.m2/settings.xml to includeorg.sonatype.mavenbook.plugins, you can install this Ant plugin by executingthe following command at the command-line:

$ mvn install[INFO] ------------------------------------------------------------------------

Writing Plugins in Alternative Languages

489

Page 509: Maven Definitive Guide De

[INFO] Building Example Ant Mojo - firstant-maven-plugin[INFO] task-segment: [install][INFO] ------------------------------------------------------------------------[INFO] [plugin:descriptor][INFO] Using 3 extractors.[INFO] Applying extractor for language: java[INFO] Extractor for language: java found 0 mojo descriptors.[INFO] Applying extractor for language: bsh[INFO] Extractor for language: bsh found 0 mojo descriptors.[INFO] Applying extractor for language: ant[INFO] Extractor for language: ant found 1 mojo descriptors....[INFO] ------------------------------------------------------------------------[INFO] BUILD SUCCESSFUL[INFO] ------------------------------------------------------------------------

Note that the plugin:descriptor goal found a single Ant mojo descriptor. To runthis goal, you would execute the following command-line:

$ mvn firstant:echo...[INFO] [firstant:echo]

echotarget:[echo] Hello Maven World

[INFO] ------------------------------------------------------------------------[INFO] BUILD SUCCESSFUL[INFO] ------------------------------------------------------------------------

The echo goal executed and printed out the default value of the message parameter.If you are used to Apache Ant build scripts, you will notice that Ant prints out thename of the target executed and then adds a logging prefix to the output of theecho Ant task.

18.3. Writing Plugins in JRubyRuby is an object-oriented scripting language which provides a rich set of facilitiesfor meta-programming and reflection. Ruby's reliance on closures and blocks makefor a programming style that is both compact and powerful. Although Ruby hasbeen around since 1993, most people came to know Ruby after it was madepopular by a Ruby-based web framework known as Ruby on Rails. JRuby is aRuby interpreter written in Java. For more information about the Ruby language,

Writing Plugins in Alternative Languages

490

Page 510: Maven Definitive Guide De

see: http://www.ruby-lang.org/, and for more information about JRuby, see:http://jruby.codehaus.org/.

18.3.1. Creating a JRuby PluginTo create a Maven plugin using JRuby, you will need to have a pom.xml and asingle Mojo implemented in Ruby. To get started, create a project directory namedfirstruby-maven-plugin. Place the following pom.xml in this directory.

Example 18.4. POM for a JRuby Maven Plugin

<project><modelVersion>4.0.0</modelVersion><groupId>org.sonatype.mavenbook.plugins</groupId><artifactId>firstruby-maven-plugin</artifactId><name>Example Ruby Mojo - firstruby-maven-plugin</name><packaging>maven-plugin</packaging><version>1.0-SNAPSHOT</version><dependencies>

<dependency><groupId>org.codehaus.mojo</groupId><artifactId>jruby-maven-plugin</artifactId><version>1.0-beta-4</version><scope>runtime</scope>

</dependency></dependencies><build>

<plugins><plugin>

<artifactId>maven-plugin-plugin</artifactId><version>2.4</version><dependencies>

<dependency><groupId>org.codehaus.mojo</groupId><artifactId>jruby-maven-plugin</artifactId><version>1.0-beta-4</version>

</dependency></dependencies>

</plugin></plugins>

</build></project>

Next, you will need to create a Mojo implemented in Ruby. Maven is going to lookfor a Ruby Mojo in ${basedir}/src/main/scripts. Put the following Ruby class

Writing Plugins in Alternative Languages

491

Page 511: Maven Definitive Guide De

in ${basedir}/src/main/scripts/echo.rb.

Example 18.5. The Echo Ruby Mojo

# Prints a message# @goal "echo"# @phase "validate"class Echo < Mojo

# @parameter type="java.lang.String" default-value="Hello Maven World" \expression="${message}"

def messageend

def executeinfo $message

end

end

run_mojo Echo

The Echo class must extend Mojo, and it must override the execute() method. Atthe end of the echo.rb file, you will need to run the mojo with "run_mojo Echo".To install this plugin, run mvn install:

$ mvn install[INFO] Scanning for projects...[INFO] ------------------------------------------------------------------------[INFO] Building Example Ruby Mojo - firstruby-maven-plugin[INFO] task-segment: [install][INFO] ------------------------------------------------------------------------...[INFO] [plugin:descriptor]...[INFO] Applying extractor for language: jruby[INFO] Ruby Mojo File: /echo.rb[INFO] Extractor for language: jruby found 1 mojo descriptors....[INFO] ------------------------------------------------------------------------[INFO] BUILD SUCCESSFUL[INFO] ------------------------------------------------------------------------

During the build, you should see that the Maven Plugin Plugin's descriptor goalapplies the JRuby extractor to create a plugin.xml which captures the annotationsin the Echo class. If you've configured your default plugin groups to include

Writing Plugins in Alternative Languages

492

Page 512: Maven Definitive Guide De

org.sonatype.mavenbook.plugins, you should be able to run this echo goal withthe following command-line:

$ mvn firstruby:echo...[INFO] [firstruby:echo][INFO] Hello Maven World...

18.3.2. Ruby Mojo ImplementationsRuby Mojos are annotated using comments in Ruby source files. A singleannotation like @parameter takes a number of attributes, and each of theseattributes must be specified on the same line. There can be no line-breaks betweenan annotations attribute in the Ruby source. Both classes and parameters areannotated. Parameters are annotated with four annotations: @parameter,@required, @readonly, and @deprecated. The @parameter attribute takes thefollowing attributes:

aliasAn alias for the parameter. An alternate name which can be used to populate thesame parameter.

default-valueProvides a default value to the parameter if the supplied value or the parameterexpression produces a null result. In echo.rb, we specify the default as "HelloMaven World".

expressionContains an expression which can resolve to a Maven property or a Systemproperty.

typeThe fully qualified Java type of the parameter. If the type is not specified it willdefault to java.lang.String.

In addition to the @parameter annotation, a parameter can take the following

Writing Plugins in Alternative Languages

493

Page 513: Maven Definitive Guide De

annotations:

@required "<true|false>"Marks the parameter as being required. The default value is false.

@readonly "<true|false>"Marks the parameter as read-only. If this is true, you may not override thedefault value or the value from the expression from the command line. Thedefault value is false.

@deprecated "<true|false>"Marks the parameter as deprecated. The default value is false.

Putting this altogether, a fully annotated message parameter from echo.rb wouldlook like the following code:

# @parameter type="java.lang.String" default-value="Hello Maven World" \expression="${message}"

# @readonly true# @required false# @deprecated falsedef messageend

Ruby Mojo classes are annotated with the following attributes:

@goalSpecifies the name of the goal.

@phaseThe default phase to bind this goal to.

@requiresDependencyResolutionTrue if the Mojo requires that dependencies be resolved before execution.

@aggregatorMarks this mojo as an aggregator.

@execute

Writing Plugins in Alternative Languages

494

Page 514: Maven Definitive Guide De

Provides the opportunity to execute a goal or lifecycle phase before executingthis Mojo. The @execute annotation takes the following attributes:

goalName of the goal to execute

phaseName of the lifecycle phase to execute

lifecycleName of the lifecycle (if other than default)

For an example of an annotated Mojo class, consider the following code example:

# Completes some build task# @goal custom-goal# @phase install# @requiresDependencyResolution false# @execute phase=compileclass CustomMojo < Mojo

...end

Mojo parameters can reference Java classes and Maven properties. The followingexample shows you how to get access to the Maven Project object from a RubyMojo.

Example 18.6. Referencing a Maven Project from a Ruby Mojo

# This is a mojo description# @goal test# @phase validateclass Test < Mojo

# @parameter type="java.lang.String" default-value="nothing" alias="a_string"def propend

# @parameter type="org.apache.maven.project.MavenProject" \expression="${project}"

# @required truedef projectend

def executeinfo "The following String was passed to prop: '#{$prop}'"info "My project artifact is: #{$project.artifactId}"

Writing Plugins in Alternative Languages

495

Page 515: Maven Definitive Guide De

endend

run_mojo Test

In the previous example, we can access properties on the Project class usingstandard Ruby syntax. If you put test.rb in firstruby-maven-plugin'ssrc/main/scripts directory, install the plugin, and then run it, you will see thefollowing output:

$ mvn install...[INFO] [plugin:descriptor][INFO] Using 3 extractors.[INFO] Applying extractor for language: java...[INFO] Applying extractor for language: jruby[INFO] Ruby Mojo File: /echo.rb[INFO] Ruby Mojo File: /test.rb[INFO] Extractor for language: jruby found 2 mojo descriptors....$ mvn firstruby:test...[INFO] [firstruby:test][INFO] The following String was passed to prop: 'nothing'[INFO] My project artifact is: firstruby-maven-plugin

18.3.3. Logging from a Ruby MojoTo log from a Ruby Mojo, call the info(), debug(), and error() methods with amessage.

# Tests Logging# @goal logtest# @phase validateclass LogTest < Mojo

def executeinfo "Prints an INFO message"error "Prints an ERROR message"debug "Prints to the Console"

end

end

run_mojo LogTest

Writing Plugins in Alternative Languages

496

Page 516: Maven Definitive Guide De

18.3.4. Raising a MojoErrorIf there is an unrecoverable error in a Ruby Mojo, you will need to raise aMojoError. Example 18.7, “Raising a MojoError from a Ruby Mojo” shows youhow to raise a MojoError. This example mojo prints out a message and then raisesa MojoError.

Example 18.7. Raising a MojoError from a Ruby Mojo

# Prints a Message# @goal error# @phase validateclass Error < Mojo

# @parameter type="java.lang.String" default-value="Hello Maven World" \expression="${message}"

# @required true# @readonly false# @deprecated falsedef messageend

def executeinfo $messageraise MojoError.new( "This Mojo Raised a MojoError" )

end

end

run_mojo Error

Running this Mojo, produces the following output:

$ mvn firstruby:error...INFO] [firstruby:error][INFO] Hello Maven World[ERROR] This Mojo Raised a MojoError

18.3.5. Referencing Plexus Components from JRuby

Writing Plugins in Alternative Languages

497

Page 517: Maven Definitive Guide De

A Ruby Mojo can depend on a Plexus component. To do this, you would use theexpression attribute of the @parameter annotation to specify a role and a hint forPlexus. The following example Ruby Mojo, depends upon an Archiver componentwhich Maven will retrieve from Plexus.

Example 18.8. Depending on a Plexus Component from a Ruby Mojo

# This mojo tests plexus integration# @goal testplexus# @phase validateclass TestPlexus < Mojo

# @parameter type="org.codehaus.plexus.archiver.Archiver" \expression="${component.org.codehaus.plexus.archiver.Archiver#zip}"

def archiverend

def executeinfo $archiver

endend

run_mojo TestPlexus

Please note that the attributes for an annotation in a Ruby Mojo cannot spanmultiple lines. If you were to run this goal, you would see Maven attempt toretrieve a component from Plexus with a role oforg.codehaus.plexus.arhiver.Archiver and a hint of zip.

18.4. Writing Plugins in GroovyGroovy is a dynamic language based on the Java Virtual Machine which compilesto Java bytecode. Groovy is a project in the Codehaus community. If you are fluentin Java, Groovy will seem like a natural choice for a scripting language. Groovytakes the features of Java, pares down the syntax a bit, and adds features likeclosures, duck-typing, and regular expressions. For more information aboutGroovy, please see the Groovy web site at http://groovy.codehaus.org.

Writing Plugins in Alternative Languages

498

Page 518: Maven Definitive Guide De

18.4.1. Creating a Groovy PluginTo create a Maven Plugin using Groovy, you only need two files: a pom.xml and asingle Mojo implemented in Groovy. To get started, create a project directorynamed firstgroovy-maven-plugin. Place the following pom.xml in this directory.

Example 18.9. POM for a Groovy Maven Plugin

<?xml version="1.0" encoding="UTF-8"?><project>

<modelVersion>4.0.0</modelVersion><groupId>org.sonatype.mavenbook.plugins</groupId><artifactId>firstgroovy-maven-plugin</artifactId><name>Example Groovy Mojo - firstgroovy-maven-plugin</name><packaging>maven-plugin</packaging><version>1.0-SNAPSHOT</version><dependencies>

<dependency><groupId>org.codehaus.mojo.groovy</groupId><artifactId>groovy-mojo-support</artifactId><version>1.0-beta-3</version>

</dependency></dependencies><build>

<plugins><plugin>

<artifactId>maven-plugin-plugin</artifactId><version>2.4</version>

</plugin><plugin>

<groupId>org.codehaus.mojo.groovy</groupId><artifactId>groovy-maven-plugin</artifactId><version>1.0-beta-3</version><extensions>true</extensions><executions>

<execution><goals>

<goal>generateStubs</goal><goal>compile</goal><goal>generateTestStubs</goal><goal>testCompile</goal>

</goals></execution>

</executions></plugin>

</plugins></build>

</project>

Writing Plugins in Alternative Languages

499

Page 519: Maven Definitive Guide De

What's going on in this POM? First, notice that the packaging of the POM ismaven-plugin because we are creating a project that will package a Maven plugin.Next, note that the project depends on the groovy-mojo-support artifact in theorg.codehaus.mojo.groovy group.

Then under src/main/groovy in a directory org/sonatype/mavenbook/plugins,create a file named EchoMojo.groovy which contains the EchoMojo class.

Example 18.10.

package org.sonatype.mavenbook.plugins

import org.codehaus.mojo.groovy.GroovyMojo

/*** Example goal which echos a message** @goal echo*/class EchoMojo extends GroovyMojo {

/*** Message to print** @parameter expression="${echo.message}"* default-value="Hello Maven World"*/String message

void execute() {log.info( message )

}}

Writing Plugins in Alternative Languages

500

Page 520: Maven Definitive Guide De

Chapter 19. Using Maven ArchetypesWarningThis chapter hasn't reached Draft status yet, it is in a pre-alpha stage. I'mpublishing works in progress because I believe that transparency inwriting benefits both the author and the community. A book is muchmore than the pages (or web pages) it is printed on, and the true meaningof a book is captured in both the content and conversation it provokes.

As this is a pre-alpha release of a chapter, don't worry about reportingtypos. Expect them until a quality beta version of this chapter is released.If you do care to provide any feedback, tell me what you want to read. If,after reading this pre-alpha chapter you are longing to know how to X, Y,or Z. Go over to our Get Satisfaction page and file a suggestion or anidea. We're very interested in the feedback.

Don't expect this chapter to be in pre-alpha for weeks and weeks, onething I'm particularly disinterested in is leaving readers with cliffhangerendings - sections that provide 95% of the essential information only toleave them with a table that hasn't been completed or a section that waswritten in a hurry. This is a new practice of "Agile Writing", and I'vetaken care to publish complete sections. While the enumeration ofthird-party plugins isn't complete and this chapter lacks a section ongenerating artifacts, the paragraphs and third-level sections that havebeen published are in this version because I didn't want to sit on thecontent for weeks and weeks.

Xpect ah lott of tipos inh this chapther(, but don't report 'em yet).

Monday, October 13, 2008 - Tim O'Brien

19.1. Introduction to Maven ArchetypesAn archetype is a template for a Maven project which is used by the Maven

501

Page 521: Maven Definitive Guide De

Archetype plugin to create new projects. Archetypes are useful for open sourceprojects such as Apache Wicket or Apache Cocoon which want to presentend-users with a set of baseline projects that can be used as a foundation for newapplications. Archetypes can also be useful within an organization that wants toencourage standards across a series of similar and related projects. If you work inan organization with a large team of developers who all need to create projectswhich follow a similar structure, you can publish an archetype that can be used byall other members of the development team. You can create a new product from anarchetype using the Maven Archetype plugin from the command line or by usingthe project creation wizard in the m2eclipse plugin introduced in Chapter 14,Maven in Eclipse: m2eclipse.

19.2. Using ArchetypesYou can use an archetype by invoking the generate goal of the Archetype pluginvia the command-line or with m2eclipse.

19.2.1. Using an Archetype from the Command LineThe following command line can be used to generate a project from the quickstartarchetype.

mvn archetype:generate \-DgroupId=org.sonatype.mavenbook \-DartifactId=quickstart \-Dversion=1.0-SNAPSHOT \-DpackageName=org.sonatype.mavenbook \-DarchetypeGroupId=org.apache.maven.archetypes \-DarchetypeArtifactId=maven-archetype-quickstart \-DarchetypeVersion=1.0 \-DinteractiveMode=false

The generate goal accepts the following parameters:

groupId

The groupId for the project you are creating.

Using Maven Archetypes

502

Page 522: Maven Definitive Guide De

artifactId

The artifactId for the project you are creating.

version

The version for the project you are creating (defaults to 1.0-SNAPSHOT).

packageName

The default package for the project you are creating (defaults to groupId).

archetypeGroupId

The groupId of the archetype you wish to use for project generation.

archetypeArtifactId

The artifactId of the archetype you wish to use for project generation.

archetypeVersion

The version of the archetype you wish to use for project generation.

interactiveMode

When the generate goal is executed in interactive mode, it will prompt the userfor all the previously listed parameters. When interactiveMode is false, thegenerate goal will use the values passed in from the command line.

Once you run the generate goal using the previously listed command line, youwill have a directory named quickstart which contains a new Maven project. Thecommand line you had to suffer through in this section is difficult to manage. Inthe next section we generate the same project running the generate goal in aninteractive mode.

19.2.2. Using the Interactive generate GoalThe simplest way to use the Maven Archetype plugin to generate a new Mavenproject from an archetype is to run the archetype:generate goal in interactivemode. When interactiveMode is set to true, the generate goal will present youwith a list of archetypes and prompt you to select an archetype and supply the

Using Maven Archetypes

503

Page 523: Maven Definitive Guide De

necessary identifiers. Since the default value of the parameter interactiveMode istrue, all you have to do to generate a new Maven project is run mvnarchetype:generate.

$ mvn archetype:generate[INFO] ------------------------------------------------------------------------[INFO] Building Maven Default Project[INFO] task-segment: [archetype:generate] (aggregator-style)[INFO] [archetype:generate][INFO] Generating project in Interactive mode[INFO] No archetype defined. Using maven-archetype-quickstartChoose archetype:1: internal -> appfuse-basic-jsf2: internal -> appfuse-basic-spring3: internal -> appfuse-basic-struts4: internal -> appfuse-basic-tapestry5: internal -> appfuse-core6: internal -> appfuse-modular-jsf7: internal -> appfuse-modular-spring8: internal -> appfuse-modular-struts9: internal -> appfuse-modular-tapestry10: internal -> maven-archetype-j2ee-simple11: internal -> maven-archetype-marmalade-mojo12: internal -> maven-archetype-mojo13: internal -> maven-archetype-portlet14: internal -> maven-archetype-profiles15: internal -> maven-archetype-quickstart16: internal -> maven-archetype-site-simple17: internal -> maven-archetype-site18: internal -> maven-archetype-webapp19: internal -> jini-service-archetype20: internal -> softeu-archetype-seam21: internal -> softeu-archetype-seam-simple22: internal -> softeu-archetype-jsf23: internal -> jpa-maven-archetype24: internal -> spring-osgi-bundle-archetype25: internal -> confluence-plugin-archetype26: internal -> jira-plugin-archetype27: internal -> maven-archetype-har28: internal -> maven-archetype-sar29: internal -> wicket-archetype-quickstart30: internal -> scala-archetype-simple31: internal -> lift-archetype-blank32: internal -> lift-archetype-basic33: internal -> cocoon-22-archetype-block-plain34: internal -> cocoon-22-archetype-block35: internal -> cocoon-22-archetype-webapp36: internal -> myfaces-archetype-helloworld37: internal -> myfaces-archetype-helloworld-facelets38: internal -> myfaces-archetype-trinidad39: internal -> myfaces-archetype-jsfcomponents40: internal -> gmaven-archetype-basic

Using Maven Archetypes

504

Page 524: Maven Definitive Guide De

41: internal -> gmaven-archetype-mojoChoose a number: 15

The first thing that the archetype:generate goal does in interactive mode is toprint out a list of archetypes that it is aware of. The Maven Archetype plugin shipswith an archetype catalog which includes a reference to all of the standard, simpleMaven archetypes (10-18). The plugin's archetype catalog also contains a numberof references to compelling third party archetypes such as archetypes which can beused to create AppFuse projects, Confluence and JIRA plugins, Wicketapplications, Scala applications, and Groovy projects. For a brief overview of thesethird-party archetypes, see Section 19.3.2, “Notable Third-Party Archetypes”.

Once you select an archetype, the Maven Archetype plugin downloads thearchetype, and then asks you to supply the following values for your new project:

• groupId

• artifactId

• version

• package

[INFO] artifact org.apache.maven.archetypes:maven-archetype-quickstart: checking for updates from centralDownloading: http://repo1.maven.org/maven2/org/apache/maven/archetypes/maven-archetype-quickstart/1.0/maven-archetype-quickstart-1.0.jar4K downloadedDefine value for groupId: : org.sonatype.mavenbookDefine value for artifactId: : quickstartDefine value for version: 1.0-SNAPSHOT: : 1.0-SNAPSHOTDefine value for package: org.sonatype.mavenbook: : org.sonatype.mavenbookConfirm properties configuration:groupId: org.sonatype.mavenbookartifactId: quickstartversion: 1.0-SNAPSHOTpackage: org.sonatype.mavenbookY: : Y

Once this interactive portion of the archetype:generate goal execution isfinished, the Maven Archetype plugin will generate the project in a directorynamed after the artifactId you supplied.

[INFO] ----------------------------------------------------------------------------[INFO] Using following parameters for creating OldArchetype: maven-archetype-quickstart:RELEASE

Using Maven Archetypes

505

Page 525: Maven Definitive Guide De

[INFO] ----------------------------------------------------------------------------[INFO] Parameter: groupId, Value: org.sonatype.mavenbook[INFO] Parameter: packageName, Value: org.sonatype.mavenbook[INFO] Parameter: basedir, Value: /Users/tobrien/tmp[INFO] Parameter: package, Value: org.sonatype.mavenbook[INFO] Parameter: version, Value: 1.0-SNAPSHOT[INFO] Parameter: artifactId, Value: quickstart[INFO] ********************* End of debug info from resources from generated POM ***********************[INFO] OldArchetype created in dir: /Users/tobrien/tmp/quickstart[INFO] ------------------------------------------------------------------------[INFO] BUILD SUCCESSFUL[INFO] ------------------------------------------------------------------------[INFO] Total time: 1 minute 57 seconds[INFO] Finished at: Sun Oct 12 15:39:14 CDT 2008[INFO] Final Memory: 8M/15M[INFO] ------------------------------------------------------------------------

19.2.3. Using an Archetype from m2eclipsem2eclipse makes creating a new Maven project from a Maven Archetype very easyby providing an intuitive wizard for searching for, selecting, and configuring aMaven Archetype. For more information about generating a Maven project form aMaven Archetype using m2eclipse, see Section 14.5.2, “Erstellen eines MavenProjekts auf der Basis eines Maven Archetyps”.

19.3. Available ArchetypesAs more and more projects adopt Maven, more and more artifacts are beingpublished by projects as a way to provide users with a quick way of creatingprojects from existing templates. This section discusses some of the simple corearchetypes from the Apache Maven as well as providing a survey of someinteresting third-party archetypes.

19.3.1. Common Maven ArchetypesSome of the most straightforward Maven archetypes are contained in theorg.apache.maven.archetypes groupId. Most of the basic archetypes underorg.apache.maven.archetypes are very basic templates that include few options.

Using Maven Archetypes

506

Page 526: Maven Definitive Guide De

You'll use them only to provide the most basic features that distinguish a Mavenproject from a non-Maven project. For example, the webapp archetype plugindescribed in this section just includes a stub of a web.xml file in${basedir}/src/main/webapp/WEB-INF, and it doesn't even go as far as providinga Servlet for you to customize. In Section 19.3.2, “Notable Third-PartyArchetypes” you'll see a quick survey of some of the more notable third-partyarchetype such as the AppFuse and Cocoon artifacts.

The following archetypes can be found in the groupIdorg.apache.maven.archetypes:

19.3.1.1. maven-archetype-quickstartThe quickstart archetype is a simple project with JAR packaging and a singledependency on JUnit. After generating a project with the quickstart archetype, youwill have a single class named App in the default package with a main() methodthat prints "Hello World!" to standard output. You will also have a single JUnit testclass named AppTest with a testApp() method with a trivial unit test.

19.3.1.2. maven-archetype-webappThis archetype creates a simple project with WAR packaging and a singledependency on JUnit. ${basedir}/src/main/webapp contains a simple shell of aweb application: an index.jsp page and the simplest possible web.xml file. Eventhough the archetype includes a dependency on JUnit, this archetype does notcreate any unit tests. If you were looking for a functional web application, thisarchetype is going to disappoint you. For more relevant web archetypes, seeSection 19.3.2, “Notable Third-Party Archetypes”.

19.3.1.3. maven-archetype-mojoThis archetype creates a simple project with maven-plugin packaging and a singlemojo class named MyMojo in the project's default package. The MyMojo classcontains a touch goal which is bound to the process-resources phase, it creates afile named touch.txt in the target/ directory of the new project when it isexecuted. The new project will have a dependency on maven-plugin-api and JUnit.

Using Maven Archetypes

507

Page 527: Maven Definitive Guide De

19.3.2. Notable Third-Party ArchetypesThis section is going to give you a brief overview of some of the archetypesavailable from third-parties not associated with the Apache Maven project. If youare looking for a more comprehensive list of available archetypes, take a look atthe list of archetypes in m2eclipse. m2eclipse allows you to create a new Mavenproject from an ever growing list of approximately 80 archetypes which span anamazing number of projects and technologies. Section 14.5.2, “Erstellen einesMaven Projekts auf der Basis eines Maven Archetyps” contains a list of archetypeswhich are immediately available to you when you use m2eclipse. The archetypeslisted in this section are available on the default list of archetypes generated by theinteractive execution of the generate goal.

19.3.2.1. AppFuseAppFuse is an application framework developed by Matt Raible. You can think ofAppFuse as something of a Rosetta Stone for a few very popular Java technologieslike the Spring Framework, Hibernate, and iBatis. Using AppFuse you can veryquickly create an end-to-end multi-tiered application that can plugin into severalfront-end web frameworks like Java Server Faces, Struts, and Tapestry. Startingwith AppFuse 2.0, Matt Raible has been transitioning the framework to Maven 2 totake advantage of the dependency management and archetype capabilities.AppFuse 2 provides the following archetypes all in the groupIdorg.appfuse.archetypes:

appfuse-basic-jsf and appfuse-modular-jsf

End-to-end application using Java Server Faces in the presentation layer

appfuse-basic-spring and appfuse-modular-spring

End-to-end application using Spring MVC in the presentation layer

appfuse-basic-struts and appfuse-modular-struts

End-to-end application using Struts 2 in the presentation layer

appfuse-basic-tapestry and appfuse-modular-tapestry

Using Maven Archetypes

508

Page 528: Maven Definitive Guide De

End-to-end application using Tapestry in the presentation layer

appfuse-core

Persistence and object model without the presentation layerArchetypes following the appfuse-basic-* pattern are entire end-to-endapplications in a single Maven project, and archetypes following theappfuse-modular-* pattern are end-to-end applications in a multimodule Mavenproject which separates the core model objects and persistence logic from the webfront-end. Here's an example from generating a project to running a webapplication for the modular Spring MVC application:

$ mvn archetype:generate \-DarchetypeArtifactId=appfuse-modular-spring \-DarchetypeGroupId=org.appfuse.archetypes \-DgroupId=org.sonatype.mavenbook \-DartifactId=mod-spring \-Dversion=1.0-SNAPSHOT \-DinteractiveMode=false[INFO] Scanning for projects...

...[INFO] [archetype:generate][INFO] Generating project in Batch mode[INFO] Archetype [org.appfuse.archetypes:appfuse-modular-spring:RELEASE] found in catalog internal[INFO] ----------------------------------------------------------------------------[INFO] Using following parameters for creating OldArchetype: appfuse-modular-spring:RELEASE[INFO] ----------------------------------------------------------------------------[INFO] Parameter: groupId, Value: org.sonatype.mavenbook[INFO] Parameter: packageName, Value: org.sonatype.mavenbook[INFO] Parameter: basedir, Value: /Users/tobrien/tmp[INFO] Parameter: package, Value: org.sonatype.mavenbook[INFO] Parameter: version, Value: 1.0-SNAPSHOT[INFO] Parameter: artifactId, Value: mod-spring...[INFO] OldArchetype created in dir: /Users/tobrien/tmp/mod-spring[INFO] ------------------------------------------------------------------------[INFO] BUILD SUCCESSFUL$ cd mod-spring$ mvn... (an overwhelming amount of activity ~5 minutes)$ cd web$ mvn jetty:run-war... (Maven Jetty plugin starts a Servlet Container on port 8080)

From generating a project with the AppFuse archetype to running a webapplication with a authentication and user-management system takes all of 5minutes. This is the real power of using a Maven Archetype as a foundation for a

Using Maven Archetypes

509

Page 529: Maven Definitive Guide De

new application. We oversimplified the AppFuse installation process a bit and leftout the important part where you download and install a MySQL database, butthat's easy enough to figure out by reading the AppFuse Quickstart Documentation.

19.3.2.2. Confluence and JIRA pluginsAtlassian has created some archetypes for people interested in developing pluginsfor both Confluence and JIRA. Confluence and JIRA are, respectively, a Wiki andan issue tracker both of which have gained a large open source user base throughgranting free licenses for open source projects. Both the jira-plugin-archetype

and the confluence-maven-archetype artifacts are under thecom.atlassian.maven.archetypes groupId. When you generate a Confluence plugin,the archetype will generate a pom.xml which contains the necessary references tothe Atlassian repositories and a dependency on the confluence artifact. Theresulting Confluence plugin project will have a single example macro class and anatlassian-plugin.xml descriptor. Generating a project from the Jira archetypecreates a project with a single, blank MyPlugin class and an atlassian-plugin.xmldescriptor in ${basedir}/src/main/resources.

fr more information about developing Confluence plugins with Maven 2, seeDeveloping Confluence Plugins with Maven 2 on the Confluence project's Wiki.For more information about developing Jira plugins with Maven 2, see How toBuild and Atlassian Plugin on the Atlassian Developer Network.

19.3.2.3. WicketApache Wicket is a component-oriented web framework which focused onmanaging the server-side state of a number of components written in Java andsimple HTML. Where a framework like Spring MVC or Ruby on Rails focuses onmerging objects within a request with a series of page templates, Wicket is verystrongly focused on capturing interactions and page structure in a series of POJOJava classes. In an age where hype-driven tech media outlets are proclaiming the"Death of Java", Wicket is a contrarian approach to the design and assembly ofweb applications. To generate a Wicket project with the Maven Archetype plugin:

$ mvn archetype:generate... (select the "wicket-archetype-quickstart" artifact from the interactive menu) ...

Using Maven Archetypes

510

Page 530: Maven Definitive Guide De

... (supply a groupId, artifactId, version, package) ...

... (assuming the artifactId is "ex-wicket") ...$ cd ex-wicket$ mvn install... (a lot of Maven activity) ...$ mvn jetty:run... (Jetty will start listening on port 8080) ...

Just like the AppFuse archetype, this archetype creates a shell web applicationwhich can be immediately executed with the Maven Jetty plugin. If you hithttp://localhost:8080/ex-wicket, you be able to see the newly created webapplication in a servlet container.

NoteThink about the power of Maven Archetypes versus the copy and pasteapproach that has characterized the last few years of web development.Six years ago, without the benefit of something like the MavenArchetype plugin, you would have had to slog through a book aboutAppFuse or a book about Wicket and followed circuitous pedagogy aboutthe framework before you could actually fire it up in servlet container. Itwas either that or just copying an existing project and customizing it foryour needs. With the Maven Archetype plugin, framework developerscan now give you a working, customized shell for an application in amatter of minutes. This is a sea change that has yet to hit the enterprisedevelopment space, and you can expect that this handful of availablethird-party artifacts will balloon to hundreds within the next few years.

19.4. Publishing ArchetypesOnce you've generated a good set of artifacts, you will probably want to share themwith the world. To do this, you'll need to create something called an Archetypecatalog. An Archetype catalog is an XML file which the Maven Archetype plugincan consult to locate archetypes in a repository. Example 19.1, “Archetype Catalogfor the Apache Cocoon Project” shows the contents of the Archetype catalog forthe Apache Cocoon project which can be found at

Using Maven Archetypes

511

Page 531: Maven Definitive Guide De

http://cocoon.apache.org/archetype-catalog.xml.

Example 19.1. Archetype Catalog for the Apache Cocoon Project

<archetype-catalog><archetypes>

<archetype><groupId>org.apache.cocoon</groupId><artifactId>cocoon-22-archetype-block-plain</artifactId><version>1.0.0</version><description>Creates an empty Cocoon block; useful if you want to add another block to a Cocoon application</description>

</archetype><archetype><groupId>org.apache.cocoon</groupId><artifactId>cocoon-22-archetype-block</artifactId><version>1.0.0</version><description>Creates a Cocoon block containing some small samples</description>

</archetype>

<archetype><groupId>org.apache.cocoon</groupId><artifactId>cocoon-22-archetype-webapp</artifactId><version>1.0.0</version><description>Creates a web application configured to host Cocoon blocks. Just add the block dependencies</description>

</archetype></archetypes>

</archetype-catalog>

To generate such a catalog, you'll need crawl a Maven repository and generate thiscatalog XML file. The Archetype plugin has a goal named crawl which does justthis, and it assumes that it has access to the file system that hosts a repository. Ifyou run archetype:crawl from the command line with no arguments, the Archetypeplugin will crawl your local repository searching for Archetypes and it will createan archetype-catalog.xml in ~/.m2/repository.

[tobrien@MACBOOK repository]$ mvn archetype:crawl[INFO] Scanning for projects...[INFO] Searching repository for plugin with prefix: 'archetype'.[INFO] ------------------------------------------------------------------------[INFO] Building Maven Default Project[INFO] task-segment: [archetype:crawl] (aggregator-style)[INFO] ------------------------------------------------------------------------[INFO] [archetype:crawl]repository /Users/tobrien/.m2/repositorycatalogFile null[INFO] Scanning /Users/tobrien/.m2/repository/ant/ant/1.5/ant-1.5.jar

Using Maven Archetypes

512

Page 532: Maven Definitive Guide De

[INFO] Scanning /Users/tobrien/.m2/repository/ant/ant/1.5.1/ant-1.5.1.jar[INFO] Scanning /Users/tobrien/.m2/repository/ant/ant/1.6/ant-1.6.jar[INFO] Scanning /Users/tobrien/.m2/repository/ant/ant/1.6.5/ant-1.6.5.jar...[INFO] Scanning /Users/tobrien/.m2/repository/xmlrpc/xmlrpc/1.2-b1/xmlrpc-1.2-b1.jar[INFO] Scanning /Users/tobrien/.m2/repository/xom/xom/1.0/xom-1.0.jar[INFO] Scanning /Users/tobrien/.m2/repository/xom/xom/1.0b3/xom-1.0b3.jar[INFO] Scanning /Users/tobrien/.m2/repository/xpp3/xpp3_min/1.1.3.4.O/xpp3_min-1.1.3.4.O.jar[INFO] ------------------------------------------------------------------------[INFO] BUILD SUCCESSFUL[INFO] ------------------------------------------------------------------------[INFO] Total time: 31 seconds[INFO] Finished at: Sun Oct 12 16:06:07 CDT 2008[INFO] Final Memory: 6M/12M[INFO] ------------------------------------------------------------------------

If you are interested in creating an Archetype catalog it is usually because you arean open source project or organization which has a set of archetypes to share.These archetypes are likely already available in a repository, and you need to crawlthis repository and generate a catalog in a file system. In other words, you'llprobably want to scan a directory on an existing Maven repository and generate anArchetype plugin at the root of the repository. To do this, you'll need to pass in thecatalog and repository parameters to the archetype:crawl goal.

The following command line assumes that you are trying to generate a catalog filein /var/www/html/archetype-catalog.xml for a repository hosted in/var/www/html/maven2.

$ mvn archetype:crawl -Dcatalog=/var/www/html/archetype-catalog.xml \[INFO] Scanning for projects...

[INFO] Searching repository for plugin with prefix: 'archetype'.[INFO] ------------------------------------------------------------------------[INFO] Building Maven Default Project[INFO] task-segment: [archetype:crawl] (aggregator-style)[INFO] ------------------------------------------------------------------------[INFO] [archetype:crawl]repository /Users/tobrien/tmp/maven2catalogFile /Users/tobrien/tmp/blah.xml[INFO] Scanning /Users/tobrien/tmp/maven2/com/discursive/cas/extend/cas-extend-client-java/2.1.1/cas-extend-client-java-2.1.1.jar[INFO] Scanning /Users/tobrien/tmp/maven2/com/discursive/cas/extend/cas-extend-client-java/2.2/cas-extend-client-java-2.2.jar-Drepository=/var/www/html/maven2...

Using Maven Archetypes

513

Page 533: Maven Definitive Guide De

Appendix A. Appendix: Detailinformationenzur settings.xml-Datei

A.1. ÜbersichtDas Element settings der setting.xml Datei beinhaltet die Elemente welchebenutzt werden, die Laufzeitumgebung von Maven zu konfigurieren. Einstellungendieser Datei sind von solcher Natur, dass diese über Projektgrenzen hinweg zumEinsatz kommen und daher nicht an ein spezifisches Projekt gebunden sein solltenoder an eine bestimmte Kundschaft verbreitet werden. Werte, welche darinenthalten sind, sind z.B. die Einstellung des lokalen Repository, alternative RemoteRepositorien und Authentifizierungsdaten. Die settings.xml kann bei einerStandardinstallation an zweierlei Orten aufgefunden werden:

• Im Maven Installationsverzeichnis: $M2_HOME/conf/settings.xml

• Als benutzerspezifische Datei des Profils: ~/.m2/settings.xmlHier eine Übersicht der Elemente der settings.xml-Datei:

Example A.1. Übersicht der Elemente einer settings.xml-Datei

<settings 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/settings-1.0.0.xsd"><localRepository/><interactiveMode/><usePluginRegistry/><offline/><pluginGroups/><servers/><mirrors/><proxies/><profiles/><activeProfiles/>

</settings>

514

Page 534: Maven Definitive Guide De

A.2. Die Details der settings.xml Datei

A.2.1. Einfache WertangabenDie Hälfte der Elemente auf oberster Ebene innerhalb des Elementes settings sindeinfache Wertangaben welche benutzt werden um das Verhalten von Maven zusteuern:

Example A.2. Einfache Wertangaben im top level Element der settings.xml

<settings 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/settings-1.0.0.xsd"><localRepository>${user.dir}/.m2/repository</localRepository><interactiveMode>true</interactiveMode><usePluginRegistry>false</usePluginRegistry><offline>false</offline><pluginGroups>

<pluginGroup>org.codehaus.mojo</pluginGroup></pluginGroups>...

</settings>

The simple top-level elements are:

Diese top-level Wertangaben sind:

localRepositoryThis value is the path of this build system's local repository. The default valueisDieser Wert bestimmt den Pfad des lokalen Repository des Build Systems. DerStandardwert ist ${user.dir}/.m2/repository

InteractiveModetrue sollte Maven auf Benutzereingaben warten, ansonsten false.Standardwert true.

Appendix: Detailinformationen zur

515

Page 535: Maven Definitive Guide De

usePluginRegistrytrue wenn Maven auf ${user.dir}/.m2/plugin-registry.xml zurückgreifensoll um die Plugin Versionen zu verwalten, Standardwert ist false.

offlinetrue sollte das Build-System im offline Modus arbeiten, Standardwert istfalse. Dieser Wert ist insbesondere wichtig für serverseitige Build-Syteme,welche nicht auf ein externes Repository zugreifen dürfen, sei dies aufgrund desNetzwerkes oder auch wegen Sicherheitsbestimmungen.

pluginGroupsDieses Element enthält eine Liste von Elementen pluginGroup, jedes enthälteine groupId. Die Liste wird durchsucht, sollte ein Plugin von derKommandozeile angezogen werden und der Parameter der groupId nicht gesetztsein. Standardmässig enthält diese Liste die org.apache.maven.plugins.

A.2.2. ServersDas Element distributionManagement eines POM definiert die Repositorien fürdie Verteilung. Gewisse Angaben, wie z.B. die Security Credentials sollten nichtmit der pom.xml Datei verteilt werden. Diese Art der Angaben sollte auf einemBuild Server in der settings.xml Datei im Element servers abgelegt werden:

Example A.3. Server Konfiguration der settings.xml

<settings 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/settings-1.0.0.xsd">...<servers>

<server><id>server001</id><username>my_login</username><password>my_password</password><privateKey>${user.home}/.ssh/id_dsa</privateKey><passphrase>some_passphrase</passphrase><filePermissions>664</filePermissions><directoryPermissions>775</directoryPermissions><configuration></configuration>

settings.xml-Datei

516

Page 536: Maven Definitive Guide De

</server></servers>...

</settings>

Die Elemente unter server sind:

idDie eindeutige Kennung - id - des Servers (nicht des Benutzers welcher beimEinloggen zum Einsatz kommt) welche mit der id des ElementesdistributionManagement des Repositories übereinstimmt.

username, passwordDiese Elemente bilden ein Paar, sie bezeichnen das Login und Passwortwelches notwendig ist, um sich gegenüber dem Server auszuweisen.

privateKey, passphraseWie vorangehend ebenfalls zwei Elemente, dieses Paar beschreibt den Pfad zueinem Privaten Schlüssel (Standardwert ist ${user.home}/.ssh/id_dsa) undeiner Passphrase, sollte diese notwendig sein. Hinweis: Zukünftig sollte esmöglich sein die Elemente passphrase und password zu externalisieren, zurZeit jedoch müssen diese in Klartext in der settings.xml enthalten sein.

filePermissions, directoryPermissionsSollte während des Deployments eine Repository Datei oder ein Verzeichnisangelegt werden, so sind dies die Rechte welche dort gesetzt werden. Diemöglichen Werte beider Parameter sind eine dreistellige *nix artige Angabeentsprechend der Dateirechte, z.B. 664, oder 775.

A.2.3. Spiegelrepositorien

Example A.4. Konfiguration eines Spiegelrepositories in der settings.xml

<settings 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

Appendix: Detailinformationen zur

517

Page 537: Maven Definitive Guide De

http://maven.apache.org/xsd/settings-1.0.0.xsd">...<mirrors>

<mirror><id>planetmirror.com</id><name>PlanetMirror Australia</name><url>http://downloads.planetmirror.com/pub/maven2</url><mirrorOf>central</mirrorOf>

</mirror></mirrors>...

</settings>

id, nameDie eindeutige Kennung, id, des Spiegelrepositories. Diese id wird benutzt, umzwischen verschiedenen Elementen mirror zu unterscheiden.

urlDie Basis URL des Spiegelrepositories. Das Build System wird diese URLeinem Repository Aufruf voransetzen, satt diese an die URL des StandardServer Repostory zu senden.

mirrorOfDie id des Servers, welcher von diesem Server gespiegelt wird. Ein Beispiel,um auf eine Spiegelrepository des Maven central Server zu verweisen(http://repo1.maven.org/maven2), setzen Sie diesen Wert auf central. DerWert muss nicht mit der id des Spiegelrepositories übereinstimmen.

A.2.4. Proxies

Example A.5. Konfiguration eines Proxy in der settings.xml

<settings 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/settings-1.0.0.xsd">...<proxies>

<proxy>

settings.xml-Datei

518

Page 538: Maven Definitive Guide De

<id>myproxy</id><active>true</active><protocol>http</protocol><host>proxy.somewhere.com</host><port>8080</port><username>proxyuser</username><password>somepassword</password><nonProxyHosts>*.google.com|ibiblio.org</nonProxyHosts>

</proxy></proxies>...

</settings>

idDie eindeutige Kennung, id, des Proxy. Diese id wird benutzt, um zwischenverschiedenen Elementen proxy zu unterscheiden

activetrue, wenn dieser Proxy aktiviert ist. Insbesondere nützlich wenn mehrereProxy konfiguriert sind, aber nur einer tatsächlich zum Einsatz kommen darf.

protocol, host, portThe protocol://host:port of the proxy, separated into discrete elements.Die Bestandteile der URL des Proxy: protocol://host:port;die Elementewerden getrennt geführt.

username, passwordDiese Elemente bilden ein Paar, sie bezeichnen das Login und Passwortwelches notwendig ist, um sich gegenüber dem Proxy auszuweisen.

nonProxyHostsUnter diesem Element befindet sich eine Liste der Ressourcen welche nichtdurch den Proxy abgebildet werden sollten. Das Trennsymbol der Liste ist dasvom Proxy Server erwartete Zeichen; in unserem Beispiel das ‚Pipe’-Symbol.Ebenfalls häufig angewandt ist die Komma getrennte Liste.

Appendix: Detailinformationen zur

519

Page 539: Maven Definitive Guide De

A.2.5. ProfilesDas Element profile der settings.xml-Datei ist ein verkürzter Auszug desElements profile der pom.xml-Datei. Es besteht aus den Elementen activation,repositories, pluginRepositories und properties. Das Element beinhaltet nurdiese vier Elemente, da diese das Build System als ganzes betreffen (welches derRolle der settings.xml entspricht) und nicht einzelne Aspekte der Projekt ModellEinstellungen.

Wird ein Profil innerhalb einer settings.xml aktiviert, so übersteuert dies jedesentsprechende Profil einer pom.xml oder profiles.xml Datei.

A.2.6. ActivationAktivierungen sind Schlüsselemente eines Profiles. Wie Profile eines POM rührtdie Mächtigkeit der Profile der settings.xml von der Möglichkeit her, Werte nurunter bestimmten Umständen/Bedingungen zu beeinflussen. Diese Bedingungenwerden im Element activation bestimmt.

Example A.6. Definition eines Activation Parameters in der settings.xml

<settings 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/settings-1.0.0.xsd">...<profiles>

<profile><id>test</id><activation>

<activeByDefault>false</activeByDefault><jdk>1.5</jdk><os>

<name>Windows XP</name><family>Windows</family><arch>x86</arch><version>5.1.2600</version>

</os><property>

<name>mavenVersion</name><value>2.0.3</value>

</property><file>

settings.xml-Datei

520

Page 540: Maven Definitive Guide De

<exists>${basedir}/file2.properties</exists><missing>${basedir}/file1.properties</missing>

</file></activation>...

</profile></profiles>...

</settings>

Die Aktivierung tritt ein, wenn alle definierten Kriterien erfüllt sind, jedoch nichtnotwendigerweise alle zur gleichen Zeit.

jdkactivation beinhaltet eine eingebaute Java spezifische Prüfung des Elementsjdk. Dieses Element wird aktiviert, sobald ein Test unter einer bestimmtenVersion eines JDK (angegeben durch ein entsprechendes Kürzel) läuft. Im obenangegebenen Beispiel wäre dies für 1.5.0_06 der Fall.

osMittels des Elements os können, wie oben ausgeführt, Betriebssystemspezifische Eigenschaften geprüft werden.

propertyDas Profil wird aktiviert, sollte Maven ein Property (ein Wert, welcherinnerhalb des POM mittels ${name} referenziert werden kann) mit dementsprechenden Wertepaar finden.

fileSchliesslich kann ein Profil durch die Existenz oder das Fehlen einerbestimmten Datei aktiviert werden.

Das Element activation ist nicht der einzige Weg, wie ein Profil aktiviert werdenkann. Die Profil id kann ebenfalls innerhalb der Datei settings.xml im ElementactiveProfile bestimmt werden. Profile können auch explizit von derBefehlszeile mittels einer Komma getrennten Liste einem P-flag folgend,aufgerufen werden (z.B. –p test).

Appendix: Detailinformationen zur

521

Page 541: Maven Definitive Guide De

Um festzustellen, welche Profile von einem bestimmten Build aktiviert werden,benutzen Sie das Maven Help Plugin.

mvn help:active-profiles

A.2.7. PropertiesMaven Properties sind ein Platzhalter für Werte - wie auch Properties unter Ant.Auf deren Werte ist mittels der Notation ${x}, x ist der Name des Properties, vonüberall innerhalb eines POM zuzugreifen. Properties kommen in fünfverschiedenen Varianten, alle zugreifbar von innerhalb der settings.xml:

env.X

Kennzeichnet man eine Variable mit vorangestelltem ‘env.’ so wird der Wertder entsprechende Shell Variablen zurückgegeben. Ein Beispiel: ${env.PATH}enthält den Wert der Umgebungsvariablen $PATH (%PATH% unter Windows).

project.x

Ein punktnotierter (.) Pfad innerhalb der pom.xml Datei wird denentsprechenden Wert enthalten.

settings.x

Ein punktnotierter (.) Pfad innerhalb der settings.xml Datei wird denentsprechenden Wert enthalten.

Java system propertiesAlle Properties welche durch java.lang.System.getProperties() offengelegtwerden sind ebenfalls als POM Properties zugänglich, so zum Beispiel dasProperty ${java.home}.

x

Innerhalb eines Elements properties oder innerhalb einer externen Dateigesetzt, ist der Wert durch ${einWert} zugänglich.

Example A.7. Setzen des Properties ${user.install} property in der

settings.xml-Datei

522

Page 542: Maven Definitive Guide De

settings.xml

<settings 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/settings-1.0.0.xsd">...<profiles>

<profile>...<properties>

<user.install>${user.dir}/our-project</user.install></properties>...

</profile></profiles>...

</settings>

Das Property ${user.install} ist für dieses POM zugänglich, wenn das Profilaktiv ist.

A.2.8. RepositoriesRepositorien sind entfernte Objektsammlungen, von welchen Maven das lokaleRepository bestückt. Von diesem lokalen Repository ruft Maven dieentsprechenden Abhängigkeiten oder Plugins ab. Verschiedene ferne Repositorienkönnen verschiedene Projekte enthalten, und mittels aktiver Profile können diesenach entsprechenden Releases und Snapshot Artefakten durchsucht werden.

Example A.8. Repository Konfiguration in der settings.xml

<settings 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/settings-1.0.0.xsd">...<profiles>

<profile>...<repositories>

<repository><id>codehausSnapshots</id>

Appendix: Detailinformationen zur

523

Page 543: Maven Definitive Guide De

<name>Codehaus Snapshots</name><releases><enabled>false</enabled><updatePolicy>always</updatePolicy><checksumPolicy>warn</checksumPolicy>

</releases><snapshots><enabled>true</enabled><updatePolicy>never</updatePolicy><checksumPolicy>fail</checksumPolicy>

</snapshots><url>http://snapshots.maven.codehaus.org/maven2</url><layout>default</layout>

</repository></repositories><pluginRepositories>

...</pluginRepositories>...

</profile></profiles>...

</settings>

releases, snapshotsZu jedem Artefakttyp release und snapshot bestehen Regeln. Mit diesen zweiGruppen hat ein POM die Möglichkeit die Regeln innerhalb eines Repositoriestypspezifisch und unabhängig von den anderen Typen anzupassen. ZumBeispiel kann man eine Einschränkung erstellen welche es nur erlaubt in derEntwicklung snapshot Artefakte herunterzuladen.

enabledtrue oder false entsprechend ob ein Repository für den entsprechenden Typzugelassen ist oder nicht (release oder snapshot)

updatePolicyDieses Element bestimmt wie oft ein Update Versuch stattfinden soll. Mavenwird den Timestamp des lokalen POM mit dem des fernen Artefaktenvergleichen. Die möglichen Werte sind: always, daily (Standardeinstellung),interval:X (wobei X eine ganze Zahl in Minuten ist) oder never

settings.xml-Datei

524

Page 544: Maven Definitive Guide De

checksumPolicyWann immer Maven eine Datei in ein Repository einstellt, wird ebenfalls eineQuersummendatei eingesetellt. Sie haben hier die Wahlmöglichkeit bezüglichignore, fail, oder warn sollte eine Quersummendatei fehlen oder fehlerhaftsein.

layoutIn der vorangegangenen Beschreibung der Repositorien wurde erwähnt, dassalle Repositorien einem gemeinsamen Layout entsprechen. Das stimmtmeistens. Maven 2 besitzt ein bestimmtes Standardlayout; jedoch baut Maven 1auf ein anderes Format auf. Benutzen Sie diesen Parameter, um zu bestimmen,ob es default oder legacy ist. Sollten Sie von Maven 1 auf Maven 2 migrieren,und Sie möchten das bisherige Repository , welches unter Maven 1 benutztwurde weiterverwenden, so setzen Sie layout auf legacy.

A.2.9. Plugin RepositoriesDie Struktur des Elementblocks pluginRepositories ist ähnlich dem desElementblocks repositories. Das Element pluginRepository spezifiziert jeweilseine entfernte Lokation, in welcher Maven Plugin Artefakte finden kann.

Example A.9. Plugin Repositories in der settings.xml

<settings 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/settings-1.0.0.xsd">...<profiles>

<profile>...<repositories>

...</repositories><pluginRepositories>

<pluginRepository><id>codehausSnapshots</id><name>Codehaus Snapshots</name><releases><enabled>false</enabled>

Appendix: Detailinformationen zur

525

Page 545: Maven Definitive Guide De

<updatePolicy>always</updatePolicy><checksumPolicy>warn</checksumPolicy>

</releases><snapshots><enabled>true</enabled><updatePolicy>never</updatePolicy><checksumPolicy>fail</checksumPolicy>

</snapshots><url>http://snapshots.maven.codehaus.org/maven2</url><layout>default</layout>

</pluginRepository></pluginRepositories>...

</profile></profiles>...

</settings>

A.2.10. Aktive Profile

Example A.10. Setzen von aktiven Profiles in der settings.xml

<settings 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/settings-1.0.0.xsd">...<activeProfiles>

<activeProfile>env-test</activeProfile></activeProfiles>

</settings>

Der letzte Baustein des settings.xml Puzzles ist das Element activeProfiles.Dieses bestimmt die Menge der Elemente activeProfile, welche alle eineneigenen Wert id haben. Jede Profile-id, welche als activeProfile definiert wurdewird immer aktiv sein, ungeachtet jeglicher Umgebungseinflüsse. Sollte keinentsprechendes Profil gefunden werden, so wird nichts passieren. So wird zumBeispiel, sollte env-test ein aktives Profil in activeProfile sein, ein Profil einerpom.xml (oder auch profile.xml) mit einer entsprechenden id aktiviert. Solltekein solches Profil gefunden werden, so wird die Verarbeitung unberührt

settings.xml-Datei

526

Page 546: Maven Definitive Guide De

fortfahren.

Appendix: Detailinformationen zur

527

Page 547: Maven Definitive Guide De

Appendix B. Appendix: Alternativen zu denSun SpezifikationenDas Apache Projekt Geronimo unterhält eine ganze Anzahl der Java EnterpriseSpezifikationen. Table B.1, “Tabelle A.1: „AlternativeImplementierungs-Artefakte der Spezifikationen“” (Tabelle A.1: „AlternativeImplementierungs-Artefakte der Spezifikationen“) führt alle vom GeronimoProjekt bereitgestellten Artefakte mit deren artifactId sowie Artifact versionauf. Um eine dieser Abhängigkeiten zu benutzen setzen Sie die groupId auforg.apache.geronimo.specs, gehen zu der Version derSpezifikationsimplementierung welche Sie benutzen möchten, und definieren Siedie Abhängigkeit indem Sie artifactId und artifact version wie in der Tabelleangegeben einsetzen.

NoteAlle Artefakte der Table B.1, “Tabelle A.1: „AlternativeImplementierungs-Artefakte der Spezifikationen“” (Tabelle A.1:„Alternative Implementierungs-Artefakte der Spezifikationen“) benutzeneine groupId von org.apache.geronimo.specs.

Table B.1. Tabelle A.1: „Alternative Implementierungs-Artefakte derSpezifikationen“

Spezifikation SpecVersion

Artifact Id ArtifactVersion

Activation 1.0.2 geronimo-activation_1.0.2_spec 1.2

Activation 1.1 geronimo-activation_1.1_spec 1.0.1

Activation 1.0 geronimo-activation_1.0_spec 1.1

CommonJ 1.1 geronimo-commonj_1.1_spec 1.0

Corba 2.3 geronimo-corba_2.3_spec 1.1

528

Page 548: Maven Definitive Guide De

Spezifikation SpecVersion

Artifact Id ArtifactVersion

Corba 3.0 geronimo-corba_3.0_spec 1.2

EJB 2.1 geronimo-ejb_2.1_spec 1.1

EJB 3.0 geronimo-ejb_3.0_spec 1.0

EL 1.0 geronimo-el_1.0_spec 1.0

Interceptor 3.0 geronimo-interceptor_3.0_spec 1.0

J2EE Connector 1.5 geronimo-j2ee-connector_1.5_spec 1.1.1

J2EE Deployment 1.1 geronimo-j2ee-deployment_1.1_spec 1.1

J2EE JACC 1.0 geronimo-j2ee-jacc_1.0_spec 1.1.1

J2EE Management 1.0 geronimo-j2ee-management_1.0_spec 1.1

J2EE Management 1.1 geronimo-j2ee-management_1.1_spec 1.0

J2EE 1.4 geronimo-j2ee_1.4_spec 1.1

JACC 1.1 geronimo-jacc_1.1_spec 1.0

JEE Deployment 1.1MR3geronimo-javaee-deployment_1.1MR3_spec 1.0

JavaMail 1.3.1 geronimo-javamail_1.3.1_spec 1.3

JavaMail 1.4 geronimo-javamail_1.4_spec 1.2

JAXR 1.0 geronimo-jaxr_1.0_spec 1.1

JAXRPC 1.1 geronimo-jaxrpc_1.1_spec 1.1

JMS 1.1 geronimo-jms_1.1_spec 1.1

JPA 3.0 geronimo-jpa_3.0_spec 1.1

JSP 2.0 geronimo-jsp_2.0_spec 1.1

JSP 2.1 geronimo-jsp_2.1_spec 1.0

JTA 1.0.1B geronimo-jta_1.0.1B_spec 1.1.1

Appendix: Alternativen zu den Sun Spezifikationen

529

Page 549: Maven Definitive Guide De

Spezifikation SpecVersion

Artifact Id ArtifactVersion

JTA 1.1 geronimo-jta_1.1_spec 1.1

QName 1.1 geronimo-qname_1.1_spec 1.1

SAAJ 1.1 geronimo-saaj_1.1_spec 1.1

Servlet 2.4 geronimo-servlet_2.4_spec 1.1.1

Servlet 2.5 geronimo-servlet_2.5_spec 1.1.1

STaX API 1.0 geronimo-stax-api_1.0_spec 1.0.1

WS Metadata 2.0 geronimo-ws-metadata_2.0_spec 1.1.1

NoteZum Zeitpunkt, zu dem Sie das Buch lesen, kann die in der Spalte"Artifact Version" aufgeführte Version bereits veraltet sein. Um dieaktuelle Versionsnummer zu überprüfen, gehen Sie mit Ihrem Browserauf http://repo1.maven.org/maven2/org/apache/geronimo/specs/ undwählen die artifactId welche Sie benutzen wollen, durch anklicken,aus. Wählen Sie die höchste Version der Spezifikation von welcher Sieabhängig sind.

Hier ein Beispiel wie Sie die Table B.1, “Tabelle A.1: „AlternativeImplementierungs-Artefakte der Spezifikationen“” (Tabelle A.1: „AlternativeImplementierungs-Artefakte der Spezifikationen“) benutzen würden: Sollten Sieeine Implementierung vornehmen, welche von der Implementierung der JTA1.0.1B Spezifikation abhängt, so würden Sie die folgende Abhängigkeitsdefinitionin ihres Projektes pom.xml-Datei einfügen:

Example B.1. Hinzufügen von JTA 1.0.1B zu einem Maven Projekt

<dependency><groupId>org.apache.geronimo.specs</groupId><artifactId>geronimo-jta_1.0.1B_spec</artifactId><version>1.1.1</version>

Appendix: Alternativen zu den Sun Spezifikationen

530

Page 550: Maven Definitive Guide De

</dependency>

Beachten Sie wie die Version des Artifaktes sich nicht mit der Version derSpezifikation deckt – die vorgehende Abhängigkeitdefinition fügt Version 1.0.1Bder JTA Spezifikation ein, benutzt aber eine Artifaktkennung von 1.1.1 (GeronimoImplementierung). Seien Sie auf der Hut, wenn Sie alternative Implementierungeneinsetzen und stellen Sie jeweils sicher, dass Sie die neuste und höchsteArtifaktversion der entsprechenden Spezifikationsversion einsetzen.

Appendix: Alternativen zu den Sun Spezifikationen

531