10 lutego 2006

Wtyczka Maven2 - assembly - przygotowanie wersji dystrybucyjnych oprogramowania

Jednym z etapów pracy w projekcie jest tworzenie wersji instalacyjnych (dystrybucji) oprogramowania. Najczęściej wykorzystywanym sposobem budowania projektu w przypadku Maven2 to wywołanie wtyczki install bądź package. Jest to wystarczający sposób na zbudowanie projektu do pliku wynikowego zgodnego z typem projektu (najczęściej jar). Jednakże wcześniej czy później pojawi się potrzeba utworzenia wersji dystrybucyjnej projektu, który ma określoną strukturę katalogów i ich zawartości. Najczęstsze formaty wersji dystrybucyjnych to zip, tar.gz, czy tar.bz2. Reprezentują one nasz projekt wraz ze wszystkim zależnościami i są wykorzystywane do dystrybucji oprogramowania, np. do instalacji na środowisku testowym, czy ostatecznie produkcyjnym.

Apache Maven2 dostarcza możliwość przygotowywania wersji dystrybucyjnych za pomocą wtyczki assembly, która na podstawie danych projektu oraz pliku konfiguracyjnego wskazanego w pom.xml tworzy tworzy plik(i) dystrybucyjne
ze zdefiniowaną strukturą katalogów i plików.

Konfiguracja wtyczki assembly odbywa się poprzez plik konfiguracyjny wskazany w sekcji project/build/plugins pliku pom.xml. W pliku znajduje się definicja wtyczki jako maven-assembly-plugin wraz z jej plikiem konfiguracyjnym, określającym strukturę plików wynikowych.
<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>pl.org.laskowski</groupId>
<artifactId>testy</artifactId>
<packaging>jar</packaging>
<version>1.0-SNAPSHOT</version>
<name>Maven Quick Start Archetype</name>
<url>http://maven.apache.org</url>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate</artifactId>
<version>3.1.2</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.0.1</version>
<configuration>
<descriptors>
<descriptor>src/main/assembly/prodenv.xml</descriptor>
<descriptor>src/main/assembly/testenv.xml</descriptor>
<descriptor>src/main/assembly/src.xml</descriptor>
</descriptors>
<finalName>${artifactId}-PROD</finalName>
</configuration>
</plugin>
</plugins>
</build>
</project>
Zgodnie z zaleceniami dotyczącymi preferowanej struktury katalogów projektów Maven2, zakłada się, że konfiguracje deskryptorów znajdują się w katalogu src/main/assembly. W powyższym przypadku wskazano kilka desktyptorów, z których prodenv.xml jest opisany poniżej. Pozostałe pliki są odmianą prodenv.xml.
<?xml version="1.0" encoding="UTF-8"?>
<assembly>
<id>prodenv</id>
<formats>
<format>zip</format>
</formats>
<fileSets>
<fileSet>
<includes>
<include>README*</include>
</includes>
</fileSet>
<fileSet>
<directory>target</directory>
<outputDirectory>lib</outputDirectory>
<includes>
<include>*.jar</include>
</includes>
<excludes>
<exclude>szczegolny.jar</exclude>
</excludes>
</fileSet>
</fileSets>
<dependencySets>
<dependencySet>
<outputDirectory>lib</outputDirectory>
<unpack>false</unpack>
<scope>runtime</scope>
<excludes>
<exclude>junit:junit</exclude>
</excludes>
</dependencySet>
</dependencySets>
</assembly>
Struktura deskryptorów wtyczek jest specyficzna dla nich samych i w przypadku wtyczki assembly umożliwia konfigurację formatów plików wynikowych (znacznik format) oraz ich zawartość. W ten sposób istnieje możliwość zdefiniowania dowolnego produktu wynikowego naszego projektu z określoną zawartością, np. dystrybucja binarna (zawierająca pełną dystrybucję projektu do uruchomienia), czy źródłowa (zawierająca źródła naszego projektu), czy też różne wersje dla różnych środowisk uruchomieniowych. Rozmieszczenie składowych pliku wynikowego konfigurowane jest przez przez znacznik fileSet, a dokładniej przez jego elementy: directory (położenie pliku źródłowego w strukturze katalogów projektu), outputDirectory (docelowe położenie pliku w strukturze katalogów w pliku wynikowym), include (podobnie jak w Ant włącza plik do pliku wynikowego), czy exclude (podobnie jak w Ant wyłącza plik z przetwarzania).
Istnieje możliwość dołączenia zależności projektu za pomocą znacznika dependencySet, co czyni proces tworzenia dystrybucji bardzo ułatwionym. Znacznik dependencySet udostępnia mechanizm dołączania zależności projektu wraz ze wszystkimi zależnościami pochodnymi (funkcjonalność oparta o przechodniość zależności, która jest nową cechą Maven2). Można wyłaczyć zależność z przetwarzania
za pomocą znacznika exclude. Część znaczników dependencySet jest analogiczna do fileSet, co sprawia, że są one intuicyjne i znacznie skracają proces definiowania struktury i wdrożenia zespołu do stosowania wtyczki.

Istnieje możliwość konfiguracji nazwy docelowej pliku wraz z odzwierciedleniem wartości zmiennych. Służy do tego znacznik finalName. W naszym przykładzie skorzystaliśmy z predefiniowanej przez Maven2 zmiennej artifactId, która zdefiniowana jest wyżej jako wartość elementu artifactId (pom.xml jest skryptem napisanym w Apache Jelly - znajomość języka jest wielce przydatna podczas pracy z Maven2, chociaż jego znaczenie znacznie zmalało w porównaniu z wcześniejszą wersją). Istnieje możliwość odwołania się do dowolnej zmiennej, które są predefiniowane przez Maven2, albo przez nas samych, np. poprzez plik settings.xml, czy opcję -D.

Uruchomienie wtyczki (a właściwie jej celu o tej samej nazwie) i zbudowanie produktu końcowego (dystrybucji) w określonym formacie i zawartości to uruchomienie następującego polecenia:
mvn assembly:assembly
Ważne jest, aby pierwsze uruchomienie polecenia było wykonane podczas podłączenia do repozytorium Maven2 tak, aby konieczne pliki zależne wtyczki mogły zostać pobrane do lokalnego repozytorium. Kolejne uruchomienia można wykonywać w trybie bezpodłączeniowym (ang. offline).

Konfiguracja wtyczki assembly w pliku pom.xml daje możliwość zdefiniowania wielu deskryptorów struktur plików wynikowych. Wybór specyficznego deskryptora polega na zdefiniowaniu zmiennej descriptorId, która wskazuje na unikalny identyfikator
w zbiorze wszystkich deskryptorów (element id w deskryptorze). Nie ma zależności między identyfikatorem a nazwą pliku deskryptora, jednakże dobrym zwyczajem jest nazywanie ich zgodnie z definiowanym deskryptorem. Możliwość definiowania różnych deskryptorów pozwala na stworzenie definicji różnych dystrybucji i ich zawartości, np. testenv - dystrybucja naszego projektu dla środowiska testowego, prodenv - dystrybucja dla środowiska produkcyjnego, etc.

Wywołanie wtyczki assembly z podaniem identyfikatora deskryptora:
mvn assembly:assembly -Dmaven.assembly.descriptorId=prodenv
Istnieje predefiniowana lista struktur plików wynikowych: src, bin i jar-with-dependencies, których użycie nie wymaga wcześniejszego definiowania w pliku pom.xml projektu.

Innym celem wtyczki assembly jest directory, który tworzy strukturę katalogową pliku wynikowego w postaci katalogu, bez finalnego pakowania dystrybucji do odpowiedniego formatu. Jest to bardzo przydatne podczas wykonywania lokalnych uruchomień projektu.
mvn assembly:directory
Wszystkie wyżej wymienione zmienne są również wykorzystywane w tym celu.