{"id":745,"date":"2015-11-08T09:00:48","date_gmt":"2015-11-08T07:00:48","guid":{"rendered":"http:\/\/invidit.de\/blog\/?p=745"},"modified":"2015-11-08T13:22:13","modified_gmt":"2015-11-08T11:22:13","slug":"konfliktmanagement-mit-maven","status":"publish","type":"post","link":"https:\/\/invidit.de\/blog\/konfliktmanagement-mit-maven\/","title":{"rendered":"Konfliktmanagement mit Maven"},"content":{"rendered":"<p>Hallo Spa\u00df-Coder.<\/p>\n<p>Nachdem wir im ersten Teil dieser Artikelreihe Maven ein wenig n\u00e4her kennenlernen durften, gehen wir nun einen Schritt weiter. Wir zeigen weitere Eigenschaften von Maven auf, die unseren Entwicklungsalltag erleichtern.<\/p>\n<p>In dieser Artikelreihe m\u00f6chten wir ein bisschen Licht ins Dunkel um das Mysterium von Apache Maven bringen. Fragen wie &#8222;Was ist Maven?&#8220;, &#8222;Wobei hilft mir Maven?&#8220; oder &#8222;Wann soll ich Maven verwenden?&#8220; werden wir beantworten. Dabei starten wir <a href=\"http:\/\/invidit.de\/blog\/mit-maven-raus-aus-der-abhaengigkeit\/\">ganz am Anfang<\/a> und versuchen die Einstiegsh\u00fcrde so klein wie m\u00f6glich zu halten. Wer jetzt noch <em>\u00e4ngstlichen Respekt<\/em> vor Maven hat, sollte die Angst nach dieser Artikelreihe abgelegt haben und nur noch <em>anerkennenden Respekt<\/em> f\u00fcr Maven empfingen.<br \/>\nUnserer Ansicht nach ist Maven im Umfeld der Java-Programmierung ein unverzichtbar hilfreiches Werkzeug.<\/p>\n<p>&nbsp;<\/p>\n<h1>Raus aus der DLL&#8230; \u00e4h&#8230;JAR-H\u00f6lle!<\/h1>\n<p>Wer von euch schon mal an gro\u00dfen Projekten gearbeitet hat, in dem die Abh\u00e4ngigkeiten nicht mit einem Werkzeug verwaltet wurden, kennt wahrscheinlich die Situation: Es gibt eine neue Version der Software, ist aber auf Teufel komm raus nicht zum Laufen zu bringen. Warum nicht? Irgendwas fehlt! Kompilierfehler, ClassNotFoundException, FileNotFoundException, &#8230; \u00e4rgerlich! Irgendwer hat mal wieder vergessen alles r\u00fcber zu kopieren. *grummel*<\/p>\n<p>Und dieser Irgendwer konnte gar nicht mal so viel daf\u00fcr. Er hat selbst lange Zeit gebraucht herauszufinden, dass f\u00fcr die neue Bibliothek A die er gebraucht hat eine zweite Bibliothek B erforderlich ist. Als es dann darum ging, die Anwendung den anderen Entwicklern (oder auf der Testumgebung) zur Verf\u00fcgung zu stellen, hatte er diese zweite Bibliothek schlicht vergessen.<\/p>\n<p>Ich m\u00f6chte das mal anhand eines Beispiels verdeutlichen. Nehmen wir mal an, wir wollen unser TicTacToe aus dem ersten Teil der Artikelreihe um eine Highscore erweitern, die wir \u00fcber einen Rest-Service ansprechen. Was wir daf\u00fcr brauchen verr\u00e4t uns Google recht schnell: Spring-Web und Jackson-Databind. Wir k\u00f6nnen uns diese Bibliotheken nun herunterladen, an einer zentralen Stelle ablegen in unserer IDE den Classpath unserer Anwendung erweitern&#8230; oder unsere POM.xml entsprechend um diese Abh\u00e4ngigkeiten erg\u00e4nzen. Wir w\u00e4hlen nat\u00fcrlich den zweiten Weg:<\/p>\n<pre class=\"lang:xhtml decode:true\" title=\"pom.xml von TicTacToe\">&lt;groupId&gt;de.invidit&lt;\/groupId&gt;\r\n&lt;artifactId&gt;tictactoe&lt;\/artifactId&gt;\r\n&lt;version&gt;0.0.1-SNAPSHOT&lt;\/version&gt;\r\n\r\n&lt;dependencies&gt;\r\n    &lt;dependency&gt;\r\n        &lt;groupId&gt;org.springframework&lt;\/groupId&gt;\r\n        &lt;artifactId&gt;spring-web&lt;\/artifactId&gt;\r\n        &lt;version&gt;4.2.2.RELEASE&lt;\/version&gt;\r\n    &lt;\/dependency&gt;\r\n    &lt;dependency&gt;\r\n        &lt;groupId&gt;com.fasterxml.jackson.core&lt;\/groupId&gt;\r\n        &lt;artifactId&gt;jackson-databind&lt;\/artifactId&gt;\r\n        &lt;version&gt;2.5.3&lt;\/version&gt;\r\n    &lt;\/dependency&gt;\r\n&lt;\/dependencies&gt;<\/pre>\n<p>Lassen wir uns nun mal den Abh\u00e4ngigkeitsgraphen dazu anzeigen, erleben wir eine \u00dcberraschung:<\/p>\n<div id=\"attachment_746\" style=\"width: 541px\" class=\"wp-caption alignnone\"><a href=\"http:\/\/invidit.de\/blog\/wp-content\/uploads\/2015\/11\/maven-transitive-dependency.png\"><img aria-describedby=\"caption-attachment-746\" decoding=\"async\" loading=\"lazy\" class=\"wp-image-746 size-full\" src=\"http:\/\/invidit.de\/blog\/wp-content\/uploads\/2015\/11\/maven-transitive-dependency.png\" alt=\"maven-transitive-dependency\" width=\"531\" height=\"320\" srcset=\"https:\/\/invidit.de\/blog\/wp-content\/uploads\/2015\/11\/maven-transitive-dependency.png 531w, https:\/\/invidit.de\/blog\/wp-content\/uploads\/2015\/11\/maven-transitive-dependency-300x181.png 300w\" sizes=\"(max-width: 531px) 100vw, 531px\" \/><\/a><p id=\"caption-attachment-746\" class=\"wp-caption-text\"><span style=\"font-size: 8pt;\">Erstellt mit IntelliJ IDEA UML-Plugin.<\/span><\/p><\/div>\n<p>Statt den erwarteten zwei Abh\u00e4ngigkeiten erhalten wir in Summe unglaubliche elf! Dabei manuell den \u00dcberblick zu behalten ist sicher nicht einfach.<\/p>\n<p>Durch das Prinzip der <em>transitiven Abh\u00e4ngigkeiten<\/em> aber nimmt Maven uns diese Last von den Schultern. Jedes Artefakt kennt seine Abh\u00e4ngigkeiten, welche wieder um seine Abh\u00e4ngigkeiten kennt, usw. Dadurch wei\u00df Maven ganz genau, welche Artefakte ben\u00f6tigt werden.<\/p>\n<p>Das Ganze funktioniert nat\u00fcrlich nur dann, wenn jedes der Artefakte ein Maven-Modul ist und eine pom.xml besitzt, die es beschreibt. Das ist aber f\u00fcr die wichtigsten Java-Projekte im Open-Source Umfeld der Fall.<\/p>\n<p>&nbsp;<\/p>\n<h2>Versionskonflikte<\/h2>\n<p>Ein weiteres Problem sind unterschiedliche Versionen der gleichen Bibliothek. Hat ein anderer Entwickler in unserem Team etwa bereits mal einen Rest-Service konsumiert und ebenfalls Spring-Web dazu verwendet, hat er dazu ggf. eine andere Version verwendet. Da wir das nicht unbedingt wissen, f\u00fchren wir eine neue Version ein.<\/p>\n<p>Auch f\u00fcr dieses Problem bietet Maven eine L\u00f6sung an. \u00dcber <em>Dependency Management <\/em>bietet Maven die M\u00f6glichkeit, die Ermittlung der Version zu zentralisieren.<\/p>\n<p>Als Entwickler gebe ich also nicht mehr an, welches Artefakt ich in welcher Version haben m\u00f6chte, sondern nur noch welches Artefakt ich ben\u00f6tige. Um die Version k\u00fcmmert sich dann das Dependency Management von Maven.<\/p>\n<p>Das obige Beispiel noch einmal mit Dependency Management:<\/p>\n<pre class=\"lang:xhtml decode:true\" title=\"pom.xml von TicTacToe\">&lt;groupId&gt;de.invidit&lt;\/groupId&gt;\r\n&lt;artifactId&gt;invid-parent&lt;\/artifactId&gt;\r\n&lt;version&gt;1&lt;\/version&gt;\r\n\r\n&lt;artifactId&gt;tictactoe&lt;\/artifactId&gt;\r\n&lt;version&gt;0.0.1-SNAPSHOT&lt;\/version&gt;\r\n\r\n&lt;dependencies&gt;\r\n    &lt;dependency&gt;\r\n        &lt;groupId&gt;org.springframework&lt;\/groupId&gt;\r\n        &lt;artifactId&gt;spring-web&lt;\/artifactId&gt;\r\n    &lt;\/dependency&gt;\r\n    &lt;dependency&gt;\r\n        &lt;groupId&gt;com.fasterxml.jackson.core&lt;\/groupId&gt;\r\n        &lt;artifactId&gt;jackson-databind&lt;\/artifactId&gt;\r\n    &lt;\/dependency&gt;\r\n&lt;\/dependencies&gt;<\/pre>\n<p>Wir geben also keine Version bei unseren Artefakten mehr an. Woher aber wird diese nun ermittelt? \u00dcber unseren Parent. Wir haben am Anfang der pom.xml einen Eintrag erg\u00e4nzt, der ein Artefakt mit dem Namen <em>invid-parent<\/em> als unseren Parent definiert. Schauen wir doch mal in die pom.xml von invid-parent.<\/p>\n<pre class=\"lang:xhtml decode:true\" title=\"pom.xml von invid-parent\">&lt;groupId&gt;de.invidit&lt;\/groupId&gt;\r\n&lt;artifactId&gt;invid-parent&lt;\/artifactId&gt;\r\n&lt;version&gt;1&lt;\/version&gt;\r\n&lt;packaging&gt;pom&lt;\/packaging&gt;\r\n\r\n&lt;dependencyManagement&gt;\r\n    &lt;dependency&gt;\r\n        &lt;groupId&gt;org.springframework&lt;\/groupId&gt;\r\n        &lt;artifactId&gt;spring-web&lt;\/artifactId&gt;\r\n        &lt;version&gt;4.2.2.RELEASE&lt;\/version&gt;\r\n    &lt;\/dependency&gt;\r\n    &lt;dependency&gt;\r\n        &lt;groupId&gt;com.fasterxml.jackson.core&lt;\/groupId&gt;\r\n        &lt;artifactId&gt;jackson-databind&lt;\/artifactId&gt;\r\n        &lt;version&gt;2.5.3&lt;\/version&gt;\r\n    &lt;\/dependency&gt;\r\n&lt;\/dependencyManagement&gt;<\/pre>\n<p>Hier haben wir also eine neue Sektion mit dem Tag <em>dependencyManagement<\/em>, in dem wir f\u00fcr alle unterliegenden POMs die Versionen festlegen.<\/p>\n<p><strong>Wichtig:<\/strong> Durch den Eintrag im Bereich <em>dependencyManagement<\/em>, haben wir keine Abh\u00e4ngigkeit festgelegt. Wird dieses Artefakt nicht noch einmal als <em>dependency<\/em> deklariert, wird Maven es niemals herunterladen und Verwenden. Eine Abh\u00e4ngigkeit im <em>dependencyManagement<\/em> sagt also nur aus: &#8222;f\u00fcr den Fall, das wir dieses Artefakt brauchen, und keine Version angegeben ist, verwende die folgende Version.&#8220;<\/p>\n<p>Treten solche Konflikte \u00fcber eine transitive Abh\u00e4ngigkeit auf, bekommen wir sie damit nat\u00fcrlich nicht in den Griff. Maven hat aber eine ausgefeilte Strategie, die beste Version f\u00fcr das Projekt zu bestimmen. Bislang hat uns diese noch keine Probleme bereitet. Von der <a href=\"https:\/\/maven.apache.org\/plugins\/maven-dependency-plugin\/examples\/resolving-conflicts-using-the-dependency-tree.html\">Apache-Maven Webseite<\/a>: &#8222;[&#8230;]by default Maven resolves version conflicts with a nearest-wins strategy.&#8220;<\/p>\n<p>&nbsp;<\/p>\n<h1>Solang du deine F\u00fc\u00dfe unter meinen Tisch stellst&#8230;<\/h1>\n<p>Aber was war das den da mit dem Parent? Wir geben in einem <em>parent<\/em> ein Artefakt an und dieses sagt uns dann was zu tun ist? Genauso wie ein strenger Vater dies tut? Nicht ganz. Genau wie die dargestellte Erziehungsmethode au\u00dfer Mode ist, l\u00e4sst sich auch unser Projekt nicht vorschreiben, was es zu tun oder zu lassen hat.<\/p>\n<p>Maven arbeitet nach dem Prinzip <em>Convention over Configuration<\/em> und macht sich das auch hier zunutze. Geben wir im vorgenannten Beispiel bei Spring-Web in unserer pom.xml eine andere Version an, als im <em>dependencyManagement<\/em> des Parent angegeben ist, gilt diese. Der Vater kann hier also nur einen Rat geben; die Entscheidung tr\u00e4gt immer noch das Kind, in diesem Fall also unser Projekt.<\/p>\n<p>Das als <em>parent<\/em> angegebene Artefakt muss existieren. Es muss dabei aber nicht einmal ein lauff\u00e4higes Java-Projekt sein. Wir sehen in unserem Parent folgende Angabe: <code>&lt;packaging&gt;pom&lt;\/packaging&gt;<\/code> Dies bewirkt, dass eben keine JAR-Datei herauskommt (das ist der Standardwert, wenn nichts angegeben ist, also die <em>Convention<\/em>), sondern wie angegeben (<em>Configuration<\/em>) eine pom.xml. Dabei sprechen wir diese mit den Maven-Koordinaten f\u00fcr ein Artefakt an, der <strong>GAV<\/strong>.<\/p>\n<p>Die Vererbung der Konfiguration erfolgt dabei \u00fcber beliebig viele Stufen. Jede pom.xml kann einen Parent haben, von dem die Konfigurationen verwendet werden, wenn keine abweichenden angegeben wurden.<\/p>\n<p>Dazu nochmal ein Beispiel. Im Standard von Maven (<em>Convention<\/em>) ist die Kompatibilit\u00e4t mit dem Java-Compiler Version 1.3. M\u00f6chten wir nun eine andere Kompiler-Version, z.B. 1.8 verwenden, erfolgt diese \u00c4nderung explizit in der pom.xml:<\/p>\n<pre class=\"lang:default decode:true\" title=\"pom.xml von invid-parent\">&lt;plugins&gt;\r\n    &lt;plugin&gt;\r\n        &lt;artifactId&gt;maven-compiler-plugin&lt;\/artifactId&gt;\r\n        &lt;version&gt;3.1&lt;\/version&gt;\r\n        &lt;configuration&gt;\r\n            &lt;source&gt;1.8&lt;\/source&gt;\r\n            &lt;target&gt;1.8&lt;\/target&gt;\r\n        &lt;\/configuration&gt;\r\n    &lt;\/plugin&gt;\r\n&lt;\/plugins&gt;\r\n<\/pre>\n<p>Alle darunterliegenden Module verwenden von da an die Kompiler-Version 1.8, bis wieder eine anders lautende Konfiguration vorgenommen wird. Was recht kompliziert klingt f\u00fchrt im Alltag dazu, dass ich mich in meinem aktuellen Projekt auf das Wesentliche konzentrieren kann. Alles globale wurde einmal f\u00fcr das Projekt sinnvoll festgelegt und muss von mir nun nicht erneut bedacht werden.<\/p>\n<p>Faustregel: Fehlt eine Angabe, schau in den Parent.<\/p>\n<p>&nbsp;<\/p>\n<h1>Strukturelle Integrit\u00e4t<\/h1>\n<p>Zum Abschluss m\u00f6chte ich noch eine Konvention vorstellen, die grundlegend ist, wenn wir neue Module anlegen, n\u00e4mlich die Verzeichnisstruktur eines Maven-Projekts. Hierin sind viele Festlegungen enthalten, die Maven verwendet um das Modul richtig zu bauen.<\/p>\n<p>Die Struktur unseres Projekts TicTacToe sieht folgenderma\u00dfen aus:<\/p>\n<table>\n<tbody>\n<tr>\n<td style=\"text-align: left; vertical-align: middle;\"><a href=\"http:\/\/invidit.de\/blog\/wp-content\/uploads\/2015\/11\/maven-folder-structure.png\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-754\" src=\"http:\/\/invidit.de\/blog\/wp-content\/uploads\/2015\/11\/maven-folder-structure.png\" alt=\"maven-folder-structure\" width=\"212\" height=\"199\" \/><\/a><\/td>\n<td style=\"text-align: left; vertical-align: top;\">\n<ul>\n<li><strong>src<\/strong> f\u00fcr Modul Sourcen\n<ul>\n<li><strong>main<\/strong> f\u00fcr eigentliche Sourcen<\/li>\n<li><strong>test<\/strong> f\u00fcr Software-Tests (junit, Selenium, \u2026)\n<ul>\n<li><strong>java<\/strong> f\u00fcr Sourcen, die mit javac kompiliert werden<\/li>\n<li><strong>resources<\/strong> f\u00fcr Dateien, die einfach in das Artefakt \u000bkopiert werden<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<\/li>\n<li><strong>target<\/strong> f\u00fcr die Ausgabe der Module nach dem Kompilieren und Bauen<\/li>\n<li><strong>pom.xml<\/strong> beschreibt unser Modul<\/li>\n<\/ul>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Ohne weitere Konfiguration ist somit klar, was das Artefakt enthalten wird und welche Tests es validieren. Dar\u00fcber hinaus sind auch die Klassenpfade f\u00fcr Kompilier- und Testphase eindeutig festgelegt. Durch die Angabe der Sprache (hier <em>java<\/em>) wird Maven angewiesen, javac f\u00fcr das Kompilieren zu verwenden. Alles was im Ordner resources abgelegt wird, landet nach dem Bauprozess im Root unseres JARs oder im passenden Ressourcen-Ordner unseres WARs.<\/p>\n<p>Diese vordefinierte Struktur kann &#8211; wie alles in Maven &#8211; auch konfigurativ anders festgelegt werden. Da wir dies aber nicht empfehlen, fehlt hier die entsprechende Anleitung dazu. F\u00fcr Legacy-Projekte, die auf Maven umgestellt werden sollen, kann es aber sinnvoll sein, wenigstens f\u00fcr eine \u00dcbergangsphase die alte Struktur beizubehalten.<\/p>\n<p>&nbsp;<\/p>\n<h1>Zusammenfassung<\/h1>\n<p>Mit den gezeigten M\u00f6glichkeiten bietet uns Maven einige Hilfsmittel um immer wiederkehrende Probleme bei gr\u00f6\u00dfer werdenden Projekten elegant in den Griff zu bekommen. Durch die Erfahrungen aus der gro\u00dfen Open-Source-Community sind die meisten von Maven verwendeten Conventions gleichzeitig Best-Practices, mit denen man die Aufgaben in aller Regel sehr gut bew\u00e4ltigen kann. Sind doch mal spezielle Einstellungen notwendig, k\u00f6nnen diese ebenfalls sehr flexibel verwendet werden.<\/p>\n<p>In Maven steckt aber noch viel mehr. Insbesondere durch Plugins l\u00e4sst sich Maven flexibel erweitern und ist auch f\u00fcr spezielle Aufgaben ger\u00fcstet. Darauf werden wir im n\u00e4chsten Artikel der Reihe n\u00e4her eingehen.<\/p>\n<p>Eure Spa\u00df-Coder<\/p>\n<p>Dieser Artikel basiert neben unseren Erfahrungen auf den Ausf\u00fchrungen aus:<\/p>\n<ul>\n<li id=\"gs_cit0\" class=\"gs_citr\" tabindex=\"0\">Maven-Training von Ren\u00e9 Gielen &#8211; <a href=\"http:\/\/it-neering.net\/\">IT Neering<\/a> (nahezu alles, was wir \u00fcber Maven wissen, wissen wir direkt oder indirekt von ihm. Danke Ren\u00e9!)<\/li>\n<li class=\"gs_citr\" tabindex=\"0\">http:\/\/maven.apache.org\/<\/li>\n<li class=\"gs_citr\" tabindex=\"0\">\n<div id=\"gs_cit0\" class=\"gs_citr\" tabindex=\"0\">H\u00fcttermann, Michael &#8211; <em>Agile ALM<\/em>, Manning Publications Co., 2011<\/div>\n<\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Hallo Spa\u00df-Coder. Nachdem wir im ersten Teil dieser Artikelreihe Maven ein wenig n\u00e4her kennenlernen durften, gehen wir nun einen Schritt weiter. Wir zeigen weitere Eigenschaften von Maven auf, die unseren Entwicklungsalltag erleichtern. In dieser Artikelreihe m\u00f6chten wir ein bisschen Licht ins Dunkel um das Mysterium von Apache Maven bringen. Fragen wie &#8222;Was ist Maven?&#8220;, &#8222;Wobei [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":757,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[22],"tags":[126,124],"_links":{"self":[{"href":"https:\/\/invidit.de\/blog\/wp-json\/wp\/v2\/posts\/745"}],"collection":[{"href":"https:\/\/invidit.de\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/invidit.de\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/invidit.de\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/invidit.de\/blog\/wp-json\/wp\/v2\/comments?post=745"}],"version-history":[{"count":13,"href":"https:\/\/invidit.de\/blog\/wp-json\/wp\/v2\/posts\/745\/revisions"}],"predecessor-version":[{"id":832,"href":"https:\/\/invidit.de\/blog\/wp-json\/wp\/v2\/posts\/745\/revisions\/832"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/invidit.de\/blog\/wp-json\/wp\/v2\/media\/757"}],"wp:attachment":[{"href":"https:\/\/invidit.de\/blog\/wp-json\/wp\/v2\/media?parent=745"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/invidit.de\/blog\/wp-json\/wp\/v2\/categories?post=745"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/invidit.de\/blog\/wp-json\/wp\/v2\/tags?post=745"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}