Pokazywanie postów oznaczonych etykietą Maven. Pokaż wszystkie posty
Pokazywanie postów oznaczonych etykietą Maven. Pokaż wszystkie posty

19 czerwca 2008

Nowe repozytorium wtyczki m2eclipse

Może mi się coś pomieszało, ale mógłbym przysiąc, że jeszcze nie tak dawno tak zwanym "update sitem" dla wtyczki integrującej Maven'a z Eclipsem było http://m2eclipse.codehaus.org/update/... a najnowsza wersja wtyczki dostępnej w tym repozytorium nie powala swoimi możliwościami. W każdym razie, aktualnym "update sitem" jest http://m2eclipse.sonatype.org/update/. Zainstalowałem nową wersję i świat od razu wygląda lepiej.

28 maja 2008

Nowości w MavenIDE dla NetBeans

Pamiętam, że ostatnio jak otworzyłem NetBeans'a (wersja 6.0) zainstalowała mi się aktualizacja MavenIDE (wersja 3.1.1), ale dopiero dziś zobaczyłem, że zmiany są istotne i ciekawe. A to co mnie urzekło, to dostępna lista archetypów. Nie dość, że lista "wbudowana" w plugin jest teraz większa, to jeszcze mamy do dyspozycji archetypy wprost z repozytoriów Mavena. Spójrzcie tylko na poniższy obrazek.

18 marca 2008

JAX-WS i Maven 2 w podejściu Contract First Development

Serwisy sieciowe, WebService’y, po przejściu fali marketingu i wyczerpaniu się na slajdach prezentacji odnajdują wreszcie swe nisze i zaczynają zyskiwać znaczenie praktyczne. Jedno jest pewne - o WebService’ach już nie tylko się mówi, coraz częściej przychodzi je zaimplementować. I pojawia się pytanie - jak to robić, żeby było – nie powiem, że łatwo jak na prezentacjach – ale chociaż znośnie. Próbowałem już wielu możliwości i śmiało mogę stwierdzić, że nie ma to jak JAX-WS (zwłaszcza 2.1) w parze z Maven 2. WebService’y z użyciem JAX-WS można implementować na dwa sposoby: zaczynając od klas Javy z odpowiednimi adnotacjami i na ich podstawie generując WSDL, albo na odwrót, zaczynając od WSDL’a i generując klasy. WSDL jest kontraktem, definicją interfejsu serwisu a podejście, w którym zaczynamy od wygenerowania klas na podstawie otrzymanego lub uprzednio napisanego WSDL’a nazywa się stąd, z angielskiego, Contract First Development. Tym właśnie stylem tworzenia serwisów sieciowych zajmę się dzisiaj.

Zakładamy więc, że mamy definicję serwisu w postaci pliku WSDL i naszym zadaniem jest implementacja takiego serwisu. Nasz WSDL (plik math.wsdl), definiujący operację dodawania wygląda następująco:

<definitions targetNamespace="http://jaxws.centric.pl/math"
xmlns:tns="http://jaxws.centric.pl/math"
xmlns="http://schemas.xmlsoap.org/wsdl/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:math="http://jaxws.centric.pl/math/domain">

<types>
<xsd:schema>
<xsd:import namespace="http://jaxws.centric.pl/math/domain"
schemaLocation="math.xsd"/>
</xsd:schema>
</types>

<message name="addRequestMsg">
<part name="addRequest" element="math:addRequest"/>
</message>

<message name="addResponseMsg">
<part name="addResponse" element="math:addResponse"/>
</message>

<portType name="mathService">
<operation name="add">
<input message="tns:addRequestMsg"/>
<output message="tns:addResponseMsg"/>
</operation>
</portType>

<binding name="soapBinding" type="tns:mathService">
<soap:binding transport="http://schemas.xmlsoap.org/soap/http"
style="document"/>
<operation name="add">
<input>
<soap:body use="literal"/>
</input>
<output>
<soap:body use="literal"/>
</output>
</operation>
</binding>

<service name="mathServiceProxy">
<port name="mathService" binding="tns:soapBinding">
<soap:address location="http://localhost:8080/mathService"/>
</port>
</service>
</definitions>

Fakt, plik jest dosyć długi, ale tak to już być musi, a doskonałą przecież wymówką jest, że język WSDL nie był tworzony z myślą o tym by być czytelnym dla ludzi. Co więcej, kto zerknął na powyższego WSDL’a zauważył, że brakuje w nim definicji typów. Owszem, zostały one zdefiniowane w osobnym pliku XML Schema a w WSDL’u tylko ten plik wskazujemy. Oto więc brakująca definicja typów (plik math.xsd):

<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://jaxws.centric.pl/math/domain"
xmlns:tns="http://jaxws.centric.pl/math/domain"
elementFormDefault="qualified">

<xsd:element name="addRequest" type="tns:addRequestType"/>

<xsd:complexType name="addRequestType">
<xsd:sequence>
<xsd:element name="a" type="xsd:double" />
<xsd:element name="b" type="xsd:double" />
</xsd:sequence>
</xsd:complexType>

<xsd:element name="addResponse" type="tns:addResponseType"/>

<xsd:complexType name="addResponseType">
<xsd:sequence>
<xsd:element name="c" type="xsd:double" />
</xsd:sequence>
</xsd:complexType>
</xsd:schema>

No dobrze, mamy już kontrakt, zatem przystępujemy do generowania klas. W tym celu utworzymy projekt Maven 2. Nie chcę zbyt dużo czasu poświęcić na podstawy Maven’a więc powiem krótko - tworzymy projekt główny, tj. typu pom o nazwie math i konfigurujemy go w następujący sposób (plik pom.xml):

<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.centric.jaxws</groupId>
<artifactId>math</artifactId>
<packaging>pom</packaging>
<version>1.1</version>
<name>math</name>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>RELEASE</version>
<configuration>
<source>1.5</source>
<target>1.5</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>RELEASE</version>
<configuration>
<encoding>US-ASCII</encoding>
</configuration>
</plugin>
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>RELEASE</version>
<configuration>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
</build>
<modules>
<module>math-schema</module>
<module>math-ws</module>
</modules>
<dependencyManagement>
<dependencies>
<dependency>
<artifactId>math-schema</artifactId>
<groupId>pl.centric.jaxws</groupId>
<version>${project.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
<pluginRepositories>
<pluginRepository>
<id>maven2-repository.dev.java.net</id>
<url>http://download.java.net/maven/2/</url>
</pluginRepository>
</pluginRepositories>
<repositories>
<repository>
<id>maven-repository.dev.java.net</id>
<url>http://download.java.net/maven/1/</url>
<layout>legacy</layout>
</repository>
<repository>
<id>maven2-repository.dev.java.net</id>
<url>http://download.java.net/maven/2/</url>
</repository>
</repositories>
</project>

Teraz tworzymy projekt potomny typu jar o nazwie math-schema. Jedynym zadaniem tego podprojektu będzie generowanie na podstawie WSDL’a i XML Schema’y odpowiednich klas, kompilowanie ich i pakowanie do postaci pliku .jar. Plik pom.xml tego projektu poniżej:

<project>
<parent>
<artifactId>math</artifactId>
<groupId>pl.centric.jaxws</groupId>
<version>1.1</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>pl.centric.jaxws</groupId>
<artifactId>math-schema</artifactId>
<name>math-schema</name>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>jaxws-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>wsimport</goal>
</goals>
</execution>
</executions>
<configuration>
<xdebug>true</xdebug>
<verbose>true</verbose>
<target>2.0</target>
</configuration>
<dependencies>
<dependency>
<groupId>com.sun.xml.ws</groupId>
<artifactId>jaxws-tools</artifactId>
<version>2.1.1</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>com.sun.xml.ws</groupId>
<artifactId>jaxws-rt</artifactId>
<version>2.1.3</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>

Kluczowe jest tutaj wykorzystanie wtyczki (ang. plugin) jaxws-maven-plugin - https://jax-ws-commons.dev.java.net/jaxws-maven-plugin/, to ona generuje klasy i w jej dokumentacji powinniśmy szukać informacji, gdy chcemy zmodyfikować standardowe ustawienia. W katalogu src projektu math-schema tworzymy podkatalog wsdl i kopiujemy tam pliki WSDL i XML Schema, czyli w naszym przypadku math.wsdl i math.xsd. Jest to domyślna lokalizacja, w której wtyczka jaxws-maven-plugin poszukuje definicji serwisów, dla których ma wygenerować klasy.

Utwórzmy teraz podprojekt typu war o nazwie math-ws. Będzie to projekt, w którym zaimplementujemy nasz serwis. Oto plik pom.xml dla tego projektu:

<project>
<parent>
<artifactId>math</artifactId>
<groupId>pl.centric.jaxws</groupId>
<version>1.1</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>pl.centric.jaxws</groupId>
<artifactId>math-ws</artifactId>
<name>math-ws</name>
<packaging>war</packaging>
<dependencies>
<dependency>
<artifactId>math-schema</artifactId>
<groupId>pl.centric.jaxws</groupId>
</dependency>
<dependency>
<groupId>com.sun.xml.ws</groupId>
<artifactId>jaxws-rt</artifactId>
<version>2.1.3</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>

Ponieważ mamy do czynienia bądź, co bądź z aplikacją internetową (ang. web application) nie obędzie się bez pliku web.xml, oto on:

<?xml version="1.0" encoding="UTF-8"?>

<web-app version="2.5"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" />

I wreszcie implementacja serwisu, tj. interfejsu wygenerowanego przez narzędzie jaxws-maven-plugin. Oto ona:

package pl.centric.jaxws.math.service;

import javax.jws.WebService;

import pl.centric.jaxws.math.MathService;
import pl.centric.jaxws.math.domain.AddRequestType;
import pl.centric.jaxws.math.domain.AddResponseType;

@WebService(endpointInterface="pl.centric.jaxws.math.MathService")
public class MathServiceBean implements MathService {

public AddResponseType add(AddRequestType addRequestType) {
AddResponseType addResponseType = new AddResponseType();

addResponseType.setC(addRequestType.getA() + addRequestType.getB());

return addResponseType;
}
}

Budujemy projekt i przystępujemy do testów. Zaczynamy od serwera GlassFish V2. Do testów używam narzędzia soapUI - http://www.soapui.org/. Prosty test pokazuje, że działa.




Ale na tym nie koniec, sprawdzamy jeszcze czy zadziała na innych serwerach. W końcu się przekonałem i wziąłem do ręki Apache Geronimo - http://geronimo.apache.org/ - w wersji 2.1. Działa bez zarzutu a serwer robi dobre wrażenie, zwłaszcza szybkość działania konsoli. Co prawda dostajemy ostrzeżenie, że nie znaleziono pliku geronimo-web.xml i może to skutkować błędami, ale nic w tym dziwnego jako że rzeczywiście takiego pliku nie ma. Błędów też nie ma. Na koniec sprawdzam jeszcze na serwerze BEA WebLogic Server 10.2. Działa i tu, co nie koniecznie musiało mieć miejsce. O ile moje doświadczenia z GlassFish’em są takie, że łyknie wszystko, co tylko można to z WLS’em jest już zupełnie na odwrót – wszystko musi być idealnie. Jak mamy WLS’a to mamy też konsolę do testowania WebService’ów, zamiast soapUI używam więc tejże konsoli. Wygląda to tak jak poniżej.


7 lutego 2008

Publikowanie do prywatnego repozytorium Maven 2

W artykule "Artifactory, czyli prywatne repozytorium Maven 2" opisałem proces instalacji i konfiguracji prywatnego repozytorium Maven 2 – Artifactory – i pokazałem jak skonfigurować klienta Maven by pobierał z niego zależności. Pozostał jeszcze do wykonania jeden krok – publikacja modułów naszego projektu do tegoż repozytorium tak, aby mogły być pobrane jako zależności innych modułów, w końcu to o to właśnie nam chodzi. Jedną z możliwości jest powierzenie zadania budowania i publikacji modułów serwerowi budowania, np. Continuum czy LuntBuild. Wtedy architektura naszego rozwiązania wygląda jakoś tak:


Ale dziś napiszę o innym rozwiązaniu, nieco uproszczonej wersji, która może być właściwsza dla nie dużych i krótkotrwałych projektów. W wersji tej nie używamy dedykowanego serwera budowania – mowa oczywiście o serwerze w sensie oprogramowania a nie maszyny – a jego role przejmują programiści i ich IDE. Wygląda to wtedy tak:


A zatem jak skonfigurować nasz projekt Maven, aby móc publikować do zdalnego, prywatnego repozytorium? Trochę zależy to od naszego repozytorium, a konkretnie od obsługiwanego przez to repozytorium protokołu komunikacyjnego. Ten artykuł dotyczy jednak Artifactory i zakłada że jego konfiguracja jest zgodna z tą opisaną w artykule cytowanym na początku. Jedyne, co trzeba zrobić to dodać do naszego pliku pom.xml następujący fragment:

<distributionManagement>
<repository>
<id>central</id>
<url>http://<host>:<port>/artifactory/libs-releases</url>
<uniqueVersion>false</uniqueVersion>
</repository>
<snapshotRepository>
<id>central</id>
<url>http://<host>:<port>/artifactory/libs-snapshots</url>
<uniqueVersion>false</uniqueVersion>
</snapshotRepository>
</distributionManagement>

Element repository określa repozytorium przechowujące wersje dystrybucyjne (ang. releases) a element snapshotRepository repozytorium przechowujące wersje tymczasowe (ang. snapshots). W moim przypadku są to odpowiednio repozytoria o nazwach libs-releases i libs-snapshots. Poniżej przytaczam ich definicje. Jest to fragment domyślnej konfiguracji, pliku konfiguracyjnego Artifactory:

<localRepository>
<key>libs-releases</key>
<description>Local repository for in-house libraries</description>
<handleReleases>true</handleReleases>
<handleSnapshots>false</handleSnapshots>
</localRepository>
<localRepository>
<key>libs-snapshots</key>
<description>Local repository for in-house snapshots</description>
<handleReleases>false</handleReleases>
<handleSnapshots>true</handleSnapshots>
</localRepository>

Podkreślę jeszcze, że jako repozytorium nie możemy tu użyć http://<host>:<port>/artifactory/repo tak jak to zrobiłem (w innym celu) w poprzednim, cytowanym na początku artykule. Jest to bowiem repozytorium wirtualne, będące swego rodzaju widokiem na sumę repozytoriów rzeczywistych i służy z tej racji tylko do odczytu. Aby opublikować tak skonfigurowany projekt wystarczy wydać polecenie 'mvn deploy'.

3 lutego 2008

Artifactory, czyli prywatne repozytorium Maven 2

Stało się, w końcu nie wytrzymałem i wziąłem się poważnie za prace nad swoim warsztatem programistycznym. Red Hat Developer Studio zirytowało mnie już wystarczająco, przesiadłem się więc na Maven 2! Muszę powiedzieć, że póki co jestem zachwycony. Zapewne z czasem przyjdą też poważne trudności i rozczarowania, ale na razie jest super, tym bardziej, że od razu zacząłem ostro i mogę zachwycać się takimi rzeczami jak chociażby integracja poprzez repozytorium kodu z serwerem automatycznego budowania (ang. Continuous Integration Server). Szybko jednak stwierdziłem, że aby sprawnie zrealizować przy pomocy Maven 2 nie trywialny projekt niezbędna jest instalacja prywatnego repozytorium. Dzisiejszy artykuł poświęcony jest właśnie temu tematowi – repozytoriom Maven, a w szczególności instalacji i konfiguracji repozytorium Artifactory.

Mam jakieś takie zamiłowanie do opisywania tematów od podstaw, zatem zacznę od paru zdań na temat repozytoriów Maven 2. Przede wszystkim, repozytorium może być zdalne, albo lokalne. Repozytorium lokalne jest to swego rodzaju pamięć podręczna rezydująca na stacji roboczej, na której uruchamiany jest klient Maven’a. Jest to po prostu struktura katalogów na naszym komputerze przechowująca biblioteki pobrane z repozytoriów zdalnych. Repozytoria zdalne dzielą się, ogólnie rzecz biorąc, na repozytoria publiczne i prywatne. Repozytoria publiczne to te udostępnione w Internecie i umożliwiające pobieranie zależności bez konieczności uwierzytelnienia się, po prostu dla każdego. Repozytorium prywatne różni się od publicznego tym, że jest dedykowane dla pewnej organizacji lub projektu a dostęp do niego jest chroniony.

Wyobraźmy sobie, że tworzymy projekt Maven 2 składający się z wielu modułów powiązanych wzajemnymi zależnościami. Jeśli moduł A potrzebuje jako zależności modułu B to deklarujemy to w pliku pom.xml modułu A i to powinno wystarczyć. W rzeczy samej, wystarczy, ale tylko pod warunkiem, że mamy do dyspozycji repozytorium prywatne, które przechowuje i udostępnia moduły tego projektu. No chyba, że jesteśmy jedynym programistą zaangażowanym w projekcie i wystarczy nam współdzielenie poprzez repozytorium lokalne, ale jest to przypadek skrajny i nieinteresujący.

Zaczynamy od pobrania Artifactory. W chwili pisania tego artykułu najnowszą wersją jest 1.2.5 i to właśnie tej wersji użyłem dla swojej instalacji. Artifactory to aplikacja WWW, którą uruchamiamy w kontenerze serwletów (ang. servlet container). Dokumentacja Artifactory mówi o dwóch opcjach instalacji: jedną jest uruchomienie na wbudowanym kontenerze Jetty a inną na dowolnym serwerze zewnętrznym implementującym odpowiednie standardy, np. na serwerze Tomcat. Ja wybrałem instalację na serwerze Tomcat 6.0.14. Sama instalacja jest procesem trywialnym, oczywiście o ile nie natrafi się na poza standardowe trudności. Ja nie natrafiłem i w zasadzie nie mam nic do dodania względem tego, co jest w dokumentacji Artifactory. Pobrane archiwum .zip rozpakowujemy w dowolnej lokalizacji, kopiujemy plik /webapps/artifactory.war do podkatalogu webapps katalogu domowego serwera Tomcat i ustawiamy zmienną artifactory.home tak aby wskazywała na katalog domowy instalacji Artifactory. To ostatnie możemy zrealizować na wiele sposobów, jednym z nich jest przypisanie zmiennej środowiskowej JAVA_OPTS lub CATALINA_OPTS wartości '-Dartifactory.home=<katalog główny Artifactory>'. To tyle, przechodzimy do konfiguracji. W tym celu edytujemy plik konfiguracyjny Artifactory /etc/artifactory.config.xml. Tutaj definiujemy repozytoria, proces tworzenia automatycznych kopii bezpieczeństwa, konfigurujemy proxy oraz co w tej chwili najważniejsze, tutaj deklarujemy, że nasze repozytorium jest rzeczywiście prywatne. Prywatne w tym sensie, że nie jest możliwy anonimowy, nie autoryzowany dostęp. Plik ten zawiera pewną domyślną konfigurację i nie musimy zmieniać zupełnie nic żeby aplikacja po prostu działała, ale powinniśmy wykonać ten jeden mały krok polegający na od komentowaniu (albo dodaniu, jeśli go niema) elementu:

<anonDownloadsAllowed>false</anonDownloadsAllowed>

Spowoduje to jak można się domyślić, że nie będzie możliwy anonimowy, nie autoryzowany dostęp do naszych zasobów. Warte odnotowania jest jeszcze to, że Artifactory dzieli repozytoria na lokalne (element localRepositories) i zdalne (element remoteRepositories). Repozytoria zdalne to częściowe kopie repozytoriów publicznych, punkt pośredni dla pobierania bibliotek a repozytoria lokalne to te, których Artifactory jest – nazwijmy to – właścicielem i administratorem. To by było na tyle jeśli chodzi o wstępną konfigurację, możemy uruchomić serwer i obejrzeć konsolę administracyjną. Uruchamiamy więc przeglądarkę, nawigujemy pod adres http://localhost:8080/artifactory i logujemy się używając identyfikatora 'admin' oraz domyślnego hasła 'password'. Musimy teraz utworzyć jakiegoś użytkownika na potrzeby klienta Maven i nadać mu uprawnienia do odczytu (rola Reader) oraz, co ważne, również do zapisu (rola Deployer) do repozytoriów. Później wyjaśnię, dlaczego. Poniżej prezentuje mój ekran konfiguracji dostępu.




Pozostała jeszcze konfiguracja klienta Maven 2. Po pierwsze, musimy Maven’owi dostarczyć identyfikator i hasło użytkownika Artifactory. W tym celu edytujemy plik konfiguracyjny /.m2/settings.xml. Dodajemy tu następujący fragment:

<servers>
<server>
<id>central</id>
<username>maven</username>
<password>passwd</password>
</server>
</servers>

Komentarza wymaga wartość elementu id. Jest to identyfikator serwera, do którego będziemy się następnie odwoływać w pliku pom.xml i może to być dowolna nazwa z tym, że akurat 'central' jest wartością specjalną, ale o tym za chwile. Edytujmy teraz plik pom.xml naszego projektu i umieśćmy tam następujący fragment:

<repositories>
<repository>
<id>central</id>
<url>http://localhost:8080/artifactory/repo</url>
</repository>
</repositories>

Ważne jest, aby element id miał tę samą wartość, co element id, dla serwera zdefiniowanego przed chwilą w pliku /.m2/settings.xml. Dodatkowo, jeśli wartość ta to 'central' to repozytorium to zasłoni repozytorium główne Maven’a. Co to oznacza? Oznacza to tyle, że wszelkie zależności Maven będzie rozwiązywał z użyciem naszego repozytorium Artifactory, które z kolei nie mogąc znaleźć odpowiednich zasobów w swoich repozytoriach lokalnych będzie ich wyszukiwało korzystając ze zdefiniowanych repozytoriów zdalnych. W takim wypadku Artifactory pobierze wpierw zależność z repozytorium zdalnego, doda ją do swojego repozytorium lokalnego i dopiero zwróci do klienta Maven inicjalizującego operację. W tym momencie powinno już być jasne, dlaczego użytkownik Artifactory wykorzystywany przez klienta Maven powinien mieć prawo do zapisu do repozytoriów. Gdyby tego prawa nie miał, nie było by możliwe pobieranie za pośrednictwem Artifactory zasobów z repozytoriów publicznych, nie było by bowiem możliwe umieszczenie artefaktu w repozytorium pośredniczącym. Pozostaje jeszcze wykonać test. W tym celu zdefiniujmy zakąś zależność od biblioteki dostępnej w repozytorium publicznym a nie obecnej jeszcze w naszym repozytorium lokalnym i uruchommy Maven’a. Jeśli wszystko jest dobrze biblioteka powinna być – oprócz tego, że pobrana – widoczna w drzewie repozytoriów Artifactory. Oto przykładowy widok: