Dobry POM

POM czyli Project Object Model, XMLowy plik koordynujący Mavenowy projekt. Wpis powie co i dlaczego powinno się w takim POMie znaleźć i powstaje, ponieważ potrzebuję takiego “dobrego punktu zaczepienia” by doń odsyłać ludzi z pytaniami.

Rzecz jest dostępna jako Gist zatem się częstujcie jeśli macie ochotę.

https://gist.githubusercontent.com/LIttleAncientForestKami/c9b185c123fc97f6022861f645766aa5/raw/45db276f570fcca357fbcf36b6209517c69c6427/pom.xml

Wszystkie znaczniki są pisane z CAPS LOCKIEM: MODELVERSION, PROJECT, ARTIFACTID.

Nagłówek

  1. Oznajmienie, że plik jest XMLem w wersji 1(widzieliście inne? Ja nie) w kodowaniu UTF8. Jedyna nie-POMowa część.
  2. XMLowy korzeń: PROJECT. W atrybutach określa gramatykę i przestrzenie nazewnicze jakie obowiązują w całym pliku, co wykorzystują IDE i inne narzędzia by oferować sensowne podpowiedzi podczas modyfikacji POMa. Dlatego m.in. nie widzicie BUILD kiedy kursor jest w REPORTING albo nie oferowane jest Wam wstawienie znacznika DEPENDENCY poza jedynymi dwoma sekcjami gdzie wstawić go (zgodnie z gramatyką) można. Linki są do przestrzeni nazw i schematu gramatyki dla modelu 4.0.0 – źle uformowany POM może użyć innego modelu niż tego podanego w następnym znaczniku.
  3. Znacznik MODEL VERSION (pisany bez spacji i w wielbłądziej notacji) ma wartość 4.0.0 dla Mavena 2 i 3. OIW, nie istnieje inny model niż własnie 4.0.0 ale ludki za Mavenem miały koncepcję, że mogą być inne wersje POMów niż ta tu opisywana.
  4. koordynaty

Koordynaty Mavena to niezłe pytanie rekrutacyjne, które mówi, czy ktoś cokolwiek o tym czytał czy po prostu używał.

Koordynaty

Maven sam z siebie nie jest taki super (niektórzy już tu powiedzą że w ogóle super nie jest ale to inny temat na inną okazję – narzędzie swoje zalety ma), ale Maven i repozytoria Mavenowe to inna broszka. Repozytoria umożliwiają Mavenowi trzymanie bez liku projektów rozmaitych, każdy ze swoim POMem i są zapleczem zarządzania zależnościami. By narzędzie wiedziało co ma ściągnąć, potrzebuje zidentyfikować projekt w czasie i przestrzeni. Do tego używa koordynatów.

 

  1. Czas to wersja (wiem, też chciałem napisać pieniądz). Ciekawe jest to, że wielu tu umieści tykającą bombę w swoim projekcie, ale o tym później. Znacznik: VERSION.
  2. Przestrzeń to grupa + artefakt… + rodzaj paczki + kwalifikator. O kwalifikatorze pewnieście nie słyszeli, bo rzadko używany. O rodzaju paczki się zapomina, bo domyślnie jest ustawiony na JAR i można go pominąć.
  3. Grupa ma notację RDNS (odwróconego DNSa), ponieważ Jawowe pakiety są tak pomyślane. Od razu dodam, jeśli chcesz coś wrzucać na Maven Central (główne repo Mavena) to chcesz mieć URLa na jaki wskazuje Twoja grupa. Czyli u mnie będzie to lafk.pl a na obrazku przy minimalnym POMie będzie to app.mycompany.com (prowadzące donikąd, zatem nie odsyłam). Znacznik: GROUPID.
  4. Artefakt to zwykle ostatni wypełniany koordynat, w obrębie grupy zwykle ostatecznie precyzujący co dokładnie będziemy ściągać. Znacznik: ARTIFACTID.
  5. Rodzaj paczkiJAR, WAR, RAR, EAR, POM. Wyjaśnię inne w osobnym wpisie, domyślnie ustawione na JAR i gdy nie podajemy znacznika PACKAGING to ustawienie owo dziedziczymy z super-POMa (zakodowanego per wersja Mavena, czyli może się zmienić kiedy podbijamy wersję Mavena na naszej maszynie albo z jakiegoś powodu wracamy do starszej niż zwykle… moja wiedza mi mówi, że jak wracamy do starszej to na razie się to ustawienie nie zmieni).

Koordynaty przekładają się na dwie rzeczy:

  1. gdzie lokalnie trzymać paczkę z daną zależnością (czy własnym artefaktem / projektem)
  2. gdzie zdalnie takiej paczki szukać (druga część tej informacji to jakie repozytoria wykorzystujemy, ale na ten wpis przyjmiemy, że to po prostu Maven Central)

Spójrzcie na zrzut ekranu z wypisem katalogów z katalogu .m2 mojej maszyny:

wypis katalogu z lokalnym repo Mavena
Lokalne repozytorium Mavena z mojej maszyny, demonstruje koordynaty

Pierwsza linia prosta to katalog .m2. Druga to podkatalog pl z grupy. Kolejne podkatalogi w grupie (jak przy pakietach) są odseparowane kropkami, aż trafiamy na ostatni, który jest artefaktem (nie, nie próbowałem tworzyć nazwy.artefaktu.z.kropkami  natomiast śmiało! eksperymentujcie sami! :D).

Wewnątrz artefaktu mamy tyle podkatalogów ile wersji żeśmy zainstalowali via mvn install albo zaciągnęli jako zależności w naszych kiedykolwiek przez Mavena obsłużonych na danej maszynie projektach.

Rodzaj paczki określa jaki plik pobieramy. Tylko POM? Tylko JAR? itp.

Nazwa pliku instalowanego lub pobieranego to: idArtefaktu-nrWersji.rodzajPaczki .

Zwróćcie uwagę, że literówka w nazwie grupy (lub jej części) może mieć nieciekawe konsekwencje: na obrazku mamy artefakty ticketMachine   i OX z DWU RÓŻNYCH grup, ponieważ ksundaysky zostało zapisane z literówką i bez i w efekcie dało dwie różne grupy. Kiedy potem będzie się człowiek do tego odwoływał w zależnościach… to niekoniecznie znajdzie. 🙂

 

Minimalny POM

Nagłówek i koordynaty razem tworzą tzw. minimalny POM – czyli POM który przejdzie walidację i zostanie uznany za POMa (przez samego Mavena). Możesz nawet dla minimalizacji wyciąć linki ze znacznika projektu, jeśli bardzo chcesz.

Minimal POM from Apache Maven docs
Minimalny POM ma XMLowy korzeń czyli znacznik PROJECT, do tego koordynaty (z domyślnym rodzajem paczki niewidocznie ustawionym na JAR) i wersję modelu 4.0.0

Znaczniki pierwszego poziomu i ich kolejność

Gramatyka POMa (ta podlinkowana w atrybutach znacznika PROJECT) określa jakie znaczniki mogą pojawić się na tzw. pierwszym poziomie – czyli jako bezpośrednie dzieci znacznika PROJECT. Wszystkie dotąd omówione znaczniki oraz kilka następnych – wszystko poza znacznikiem PROJECT który nazywam też korzeniem, są takimi właśnie znacznikami.

Oznacza to, że mógłbym je umieścić w innej kolejności. To mogą być znaczniki pisane zaraz przed zamknięciem znacznika PROJECT. Ale zwykło się je umieszczać na starcie. Taka konwencja dla ludzi. Podobnie znacznik BUILD o którym za chwilę, umieszcza się po znaczniku DEPENDENCIES a przed REPORTING. Kolejność wszystkich znaczników jest u mnie zaczerpnięta z dokumentacji Mavena.

Gest w stronę ludzi

Jako gest w stronę ludzi do mojego POMa dołączyłem znaczniki NAME, DESCRIPTION i URL (który niezbyt dobrze wykorzystuję). Nie są one potrzebne Mavenowi czy narzędziom, ale to trzy podstawowe znaczniki z szeregu tych jakie Mavenowcy przewidzieli by opisać swój projekt.

Tak, Mavenowcy chcieliby, abyście myśleli o Mavenie nie tylko jako narzędziu zarządzania zależnościami. Dla nich cykl `site` jest istotny i używają swego narzędzia do tworzenia Apaczowych stron, budowy projektów, koordynacji pracy nad nimi itp. Ale ad rem, wracamy do dobrego POMa. By ludzie mogli się zorientować co właściwie oglądają i by w Maven Central albo na stronach robionych przez inne automaty rzeczy ładnie wyglądały dodaje się:

  1. NAME – czyli nazwę projektu, która sprowadza się do ARTEFACTID dla ludzi, czyli np. ze znakami niedozwolonymi w ID artefaktu (np. spacje),
  2. DESCRIPTION – czyli jednozdaniowy opis podsumowujący projekt.
  3. URL – czyli stronę gdzie można zasięgnąć więcej informacji o projekcie. Która w moim szablonie zawsze skierowuje do mojej strony, gdzie o danym projekcie zwykle nic nie ma. Stąd wcześniejsza uwaga, że niezbyt dobrze ją wykorzystuję. Jeśli coś wrzucacie do Maven Central, jednym ze sprawdzeń jest “czy macie prawo do tej strony”. Znam sporo projektów, które skierowują do swojego repo na GitLabie czy innym GitHubie… Co nie jest pozbawione sensu, ale nie jest to strona pod ICH kontrolą, bo jest w domenie GitLaba GitHuba itp.

Skoro to dla ludzi to można pominąć i maszyny się nie pogniewają. Jakbyście spróbowali pominąć coś dla maszyn to będzie że źle złożony POM, ale te trzy można pominąć bez konsekwencji innej niż “nie wiem co to za projekt” i nieco brzydszej strony o projekcie jaką Maven potrafi wygenerować.

Własności

czyli PROPERTIES. Bardzo ciekawa sekcja, gdzie możemy definiować własności naszego POMa… czyli zmienne. By nie wpaść w dywagacje nieistotne z perspektywy po prostu opisu dobrego POMa, powiem tak:

  1. W wersji minimum umieszczam tutaj ustawienie umożliwiające uniezależnienie budowy od platformy i ustawiam Mavenowej wtyczce kompilatora wersję Jawy jakiej ma używać do kompilacji.
  2. Normalnie umieszczam tu wszystkie numerki wersji, by mieć je w jednym miejscu.
  3. Zgłębiwszy kiedyś kwestie kodowania znaków w Mavenie, zamieszciłem tu również wszystkie inne UTFowe ustawienia.

Trzy istotne rzeczy:

  1. to co tu ustawiasz, możesz wykorzystać jako zmienne, w notacji ${nazwa}  – jak to robię w przypadku numerków wersji
  2. o ile Maven zrozumie ustawienie kodowania plików źródłowych via project.build.sourceEncoding  a wtyka kompilatora zrozumie ustawienie maven.compiler.source  czy target ale już wersji tak nie ustawię, dlatego muszę i tak wtykę zadeklarować potem, tylko po to by podać jej wersję.
  3. własności są dziedziczone – Twój projekt dziedziczy własności z rodzica i przekazuje je (i swoje) dalej.

Kolejność tej sekcji zależy od praktyk zespołu. Zwykle widuję ją na początku POMa. Ale są ludzie lubiący dawać ją na końcu. Dla parsowania zmiennych OIW nie ma to znaczenia.

Minimalne własności

 

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.target>1.8</maven.compiler.target>
        <maven.compiler.source>1.8</maven.compiler.source>
    </properties>


Zależności

czyli DEPENDENCIES, sekcja zawierająca pełno zależności. Każde DEPENDENCY to jeden zaciągany projekt Mavenowy – lub więcej! Więcej jeśli projekt ma własne zależności. O tym w osobnym wpisie, ponieważ… u mnie to miejsce jest puste.

Celowo puste! Tak, by każdy mógł wstawić własne kombo narzędzi jakie chce zaciągnąć. Jakbym coś miał wstawiać to w pierwszej kolejności byłoby to TestNG i pewnie się tam kiedyś znajdzie, ale IDE też prosto je dodać potrafi zatem…

Każda zależność to zestaw koordynatów – nasz projekt można wstawić gdzieś jako zależność podając jego koordynaty, zatem uczciwie jest, że działa to również w drugą stronę – my możemy zaciągnąć cudzy projekt do naszego lokalnego repozytorium po jego koordynatach.

Jedyny dozwolony przez model POMa w wersji 4 znacznik w sekcji DEPENDENCIES to DEPENDENCY. Nie macie innej opcji jeśli chcecie być w zgodzie z gramatyką.

Zarządzanie zależnościami?

Wszelkie znaczniki COKOLWIEK MANAGEMENT (pisane bez spacji i wielbłądzią notacją) odstawcie na bok, chyba, że robicie POMa pod dziedziczenie. Zatem DEPENDENCYMANAGEMENT wylatuje, DEPENDENCIES zostaje. Podobnie z PLUGINMANAGEMENT i PLUGINS.

Budowa

czyli BUILD. Tu mamy więcej opcji, ale ja mam jedną: PLUGINS. W tej zaś mogę mieć tylko PLUGIN. Zasada podoba jak przy zależnościach, ponieważ Maven wtyczkami stoi. Każda wtyka to jeden wpis PLUGIN, wszystkie wtyki muszą zawierać się w PLUGINS. Wtyki tu ustawione dbają o:

  1. posiadanie odpowiedniej wersji Mavena (i Jawy, choć z tego zrezygnowałem i nie pamiętam nawet czemu) – “enforcer”
  2. łatwe odpalenie spakowanej apki ze słoika; tak po prostu, bez zgadywania czy szukania głównej klasy czy bujania się z manifestem – “jar” (można tu się pobawić konfiguracją lepiej niż ja to zrobiłem, o czym kiedy indziej)
  3. ustawienie Jawy użytej do kompilacji na nowszą niż domyślna. Ta w Mavenie 3 jest ustawiona na JDK 5, a w jednej z ostatnich wersji na 6. Tak. Podbili na JDK 6, w czasach, gdzie 8 kończy swój żywot.
  4. odpowiednią dla wtyki jar wersję wtyki do zasobów (byś mógł je ładnie pakować do słoika).

Jeśli nie masz nic w katalogach z zasobami to ta ostatnia wtyka Ci się nie przyda. Jeśli masz projekt, który można budować dowolnym w miarę nowoczesnym Mavenem 3 a nawet jak spróbują budować go 2 to nie stanie się nic złego, to odpada Ci pierwsza wtyka. Moim zdaniem dwie pozostałe to kombo wymagane. Słoik możesz zastąpić, jeśli budujesz go w inny sposób (inną wtyką) ale raczej nie powinieneś go wycinać BEZ zastępowania. Kompilator… no tylko jeśli nie lubisz pisać kodu z rzeczami z JDK 7-13. Tak, 13tka już jest publicznie dostępna.

Po co deklarować wtykę tylko dla deklarowania jej wersji?

Słuszne pytanie. Otóż inaczej bierzesz wersję wtyki z super-POMa. Czyli tę wszytą i od wersji Mavena zależną. Ale głównie to boli, bo ta wersja często jest starsza niż ta dostępna obecnie. Co – jeśli np. masz automaty do sprawdzania wersji zależności i wtyczek w swoich POMach – powoduje, że automaty trąbią o przestarzałych wersjach.

 

Cała sekcja dotyczy cyklu budowy. Jeśli chcesz wtyki do raportowania… to inną razą, bo w tym POMie ich niet.

 

Podsumowanie

Opisałem nagłówek, znaczniki na które zwykle w ogóle się nie zwraca uwagi jak i te ciekawsze, jak koordynaty projektu, czy własności POMa, które można wykorzystać by nie mieć ostrzeżenia o budowie zależnej od platformy (przez ustawienie kodowania źródeł) albo określić jaką Jawą kompilujemy. Jest odpowiedź na pytanie o różnicę między znacznikami zwykłymi i tymi z ‘management’ w nazwie. Jest minimalny POM i kombo wtyczek by budowa dobrze poszła i słoik się odpalił tak ot.

Tyle w temacie, mam nadzieję, że się przyda i że coś z tego było ciekawe lub pouczające. Chętnie powitam komcie by wiedzieć co można dodać / usprawnić.

Całość raz jeszcze, jako Gist. Postaram się w razie podbicia wersji, zaktualizować wpis. O ile będę pamiętał… 😉

Advertisements

2 Comments

  1. Fajny artykuł. Jedna kwestia, dependencyManagment można używać bez dzieci do wymuszania wersji zależności pośrednich (transitive)

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s