M2: tworzenie wersji dystrybucyjnej
Hmmm…
maven to elastyczne narzędzie, żeby osiągnąć zamierzony cel trzeba czasami przebić się przez całkiem sporo nakonfigurować. W dzisiejszym odcinku
będzie o trzech pluginach: assembly, jar i antrun. W maven 1.x było wszechobecne Jelly, chciałem się przekonać jak będzie wyglądało tworzenie wersji dystrybucyjnej w m2.
Zacznijmy od projektu który wygenerowaliśmy poprzednim razem, jeżeli chcielibyśmy wygenerować teraz jar’a wystarczy wydać polecenie mvn package. M2 bez żadnych problemów stworzy nam jar’a, teraz dodajmy do naszego projektu jakieś zasoby. Aby zasoby były bezproblemowo dołączone do jar’a musimy stworzyć katalog $project.base.dir/src/main/resources i wszystkie zasoby wrzucać właśnie do niego. Po utworzeniu jar’a zostaną one przekopiowane do podstawowej ścieżki archiwum.
Gdy zajdzie konieczność zmiany manifestu jar’a to wedle mojej skromnej wiedzy musimy pogrzebać trochę w konfiguracji maven-jar-plugin. Można to zrobić specyfikując w pom.xml następujący blok:
<build>
...
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<addExtensions>true</addExtensions>
<classpathPrefix>./lib/</classpathPrefix>
<mainClass>org.grejpfrut.App</mainClass>
<packageName>org.grejpfrut</packageName>
</manifest>
</archive>
</configuration>
</plugin>
</plugins>
...
<build>
Cała magia siedzi wewnątrz sekcji <configuration> tutaj specyfikujemy, że do manifestu ma być dodana informacja o classpath, tag classpathPrefix instruuje maven-jar-plugin aby przed każdą zależnością dostawiał ścieżke ./lib (np. ./lib/log4j-1.2.13.jar). Takie rozwiązanie pozwala utrzymać porządek w katalogu dystrybucji i rozdzielić główny jar od jego zależności. Pozostałe parametry nie wymagają wyjaśnień.
Aby sprawdzić poprawność manifestu możemy użyć kodu App.java który podałem w poście o maven 1.x, należy uaktualnić plik pom.xml o opis log4j w sekcji dependencies i wydać polecenie mvn package. Po rozpakowaniu jar’a możemy podejrzeć plik Meta-inf/Manifest.mf. W katalogu Meta-inf maven umieszcza kopie pom.xml oraz plik pom.properties który zawiera najbardziej podstawowe informacje o projekcie (wersja, artifactId, groupId). Po co te pliki?
“The pom.xml and pom.properties files are packaged up in the JAR so that each artifact produced by Maven is self-describing and also allows you to utilize the metadata in your own application if the need arises. One simple use might be to retrieve the version of your application.”maven.apache.org
Wyobrażmy sobie jednak, że pojawia się kolejny problem… mianowicie w przypadku zasobów związanych z umiedzynarodowieniem naszej aplikacji bywa, że properties muszą znajdować się w katalogu src w katalogu związanym z odpowiednim pakietem. Problem ten wystąpi zawsze gdy będziemy chcieli umieścić zasoby w katalogu innym niż wposmniane wcześniej src/main/resources (bądź działającym na tej podobnej zasadzie src/test/resources). W czasie tworzenia jar’a m2 po prostu nie przekopiuje niczego poza skompilowanymi klasami. Zastosowane przez nas rozwiązanie zostało oparte o ant’a.
W m2 wszystko związane jest z fazami i cyklem zycia oprogramowania. W starym mavenie specyfikowaliśmy sekcje post i pregoal w maven.xml. W m2 aby użyć jakiegoś celu/wtyczki który nie należy do zdefiniowanego cyklu budowania musimy go zadeklarować w pom.xml. Tak też musimy postąpić z maven-antrun-plugin.
<plugin>
<artifactId>maven-antrun-plugin</artifactId>
<executions>
<execution>
<phase>compile</phase>
<configuration>
<tasks>
<copy todir="${basedir}/target/classes/">
<fileset dir="${basedir}/src/main/java/">
<include name="**/lang/*.properties">
</fileset>
</copy>
</tasks>
</configuration>
<goals>
<goal>run</goal>
</goals>
</execution>
</executions>
</plugin>
Plugin antrun pozwala wykonać dowolne zadania ant’a i przypisać je do konkretnego etapu tworzenia oprogramowania. Widzimy tutaj prosty skrypt który wykonuje to czego m2 nie robi.
Jesteśmy coraz bliżej działającej dystrybucji
. Do tworzenia dystrybucji używa się wtyczki assembly. Jest ona całkiem przyzwoicie opisana, więc w razie pytań warto tam zajrzeć. Poza tym naprawde dobrze opisał to Jacek Laskowski. Mój opis będzie więc zgrubny, ogranicze się tylko do wypisania tego co zmieniłem w stosunku do tego co zaporoponował Jacek. Oto co zostało dodane do pom.xml:
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.0.1</version>
<configuration>
<descriptors>
<descriptor>src/main/assembly/jar.xml</descriptor>
</descriptors>
<finalName>${artifactId}</finalName>
</configuration>
</plugin>
Jedyną ciekawą rzeczą w tym kawałku xml’a jest specyfikacja deskryptora. Standardowo deskryptory dla assembly składowane są w src/main/assembly, nasz projekt może być dystrybuowany na więcej niż jeden sposobów. To w jaki sposób każdy z nich będzie tworzony zależy właśnie od opisu. Dokładny opis możliwości i predefiniowanych deskryptorów na stronie pluginu.
Deskryptor jar.xml musi zostać dodany do projektu w ścieżce która specyfikujemy w pom.xml.
<assembly>
<id>zip</id>
<formats>
<format>jar</format>
</formats>
<includeBaseDirectory>false</includeBaseDirectory>
<fileSets>
<fileSet>
<directory>target</directory>
<outputDirectory></outputDirectory>
<includes>
<include>*.jar</include>
</includes>
</fileSet>
<fileSet>
<directory>lib</directory>
<outputDirectory></outputDirectory>
<includes>
<include>*.dll</include>
</includes>
</fileSet>
</fileSets>
<dependencySets>
<dependencySet>
<outputDirectory>/lib</outputDirectory>
<unpack>false</unpack>
<scope>runtime</scope>
</dependencySet>
</dependencySets>
</assembly>
Tu będzie troche komentarza, określamy format dystrybucji (oprócz zip’a może być jeszcze chyba tar i jar) tutaj zip. Pierwszy fileset załącza do dystrybucji nasz jar, wygenerowany gdzieś około pierwszego akapitu tego artykułu. Znajdzie sie on w katalogu /target/ i zostanie przekopiowany do głównego katalogu dystrybucji. W Weed używamy kilku dll’i, który na codzień mieszkają w $project.base.dir/lib zostaną również przekopiowane do katalogu lib. Następnie włączamy do dystrybucji wszystkie zależności, zgodnie z opisaną wcześniej filozofią zostaną one przeniesione do lib. Przy specyfikacji dependencySet ważne jest aby zaznaczyć jaki scope mają mieć zależności włączane do dist’a (tutaj np. nie zostanie włączony junit który ma scope test).
To właściwie wszystko
. Prawda jakie to proste
. teraz mvn assembly:assembly dostaniemy ładnego zip’a którego po rozpakowaniu możemy uruchomić gdzie nam się spodoba. Drugi wart zapamiętania cel to mvn assembly:directory tworzy on całą strukturę, ale nie pakuje jej do zip’a. Niezłe do testowania. Uruchamiamy : java -jar test-1.0-SNAPSHOT.jar
Problem: niewiem czemu zależności które mają scope : system, nie są uwzględniane w manifeście przy tworzeniu jar’a. jeżeli ktoś wie dlaczego i jak sobie z tym poradzić będę wdzięczny. ![]()