czwartek, 19 lutego 2009

Web serwis top-down, JAX-WS, Eclipse WTP, Maven 2, Apache Tomcat 6.0

Wstęp

Tematem wpisu są problemy i pytania napotykane podczas tworzenia web serwisu metodologią top-down z wykorzystaniem JAX-WS, Eclipse WTP i Maven 2 rozpraszanego na serwerze Apache Tomcat 6.0.

Nie jest to typowy how-to, nie opisuję wszystkiego krok po kroku, ponieważ nie wydaje mi się to konieczne. Koncentruję się natomiast na konkretnych problemach, z jakimi się trzeba zmierzyć.

Założenia
1. Wersja Javy: 1.6
2. IDE: Eclipse 3.3
3. Narzędzie budowania projektu: Maven 2 (plug-in m2eclipse)
4. Metodologia: Top-down
5. Implementacja web serwisu: JAX-WS
6. Serwer: Apache Tomcat 6.0

Kroki do wykonania
1. Utworzenie projektu w Eclipse
2. Skonfigurowanie Mavena
3. Utworzenie definicji web serwisu
4. Wygenerowanie klas na podstawie definicji web serwisu
5. Podłączenie wygenerowanych klas jako źródeł w projekcie Eclipse
6. Zaimplementowanie web serwisu
7. Utworzenie deskryptorów
8. Przetestowanie działania web serwisu

Problemy i kwestie do rozstrzygnięcia
1. Jak pożenić Eclipse WTP z Maven 2?
2. Jak skonfigurować JAX-WS w Maven 2?
- Jaki plugin zastosować?
- Gdzie plugin się znajduje?
- Gdzie umieścić plik WSDL?
3. Jak zidentyfikować błędy wsimport?
4. Jak utworzyć implementację web serwisu?
- Jakich argumentów adnotacji @WebService użyć?
5. Jak utworzyć deskryptory?
- Których deskryptorów trzeba użyć?
- Jaka ma być zawartość deskryptorów?

Eclipse WTP + Maven 2

Maven jest bezdyskusyjnie bardzo dobrym rozwiązaniem podczas automatycznego budowania projektów, w szczególności w przypadku stosowania continuous integration z wykorzystaniem Continuum czy Cruise Control. Jednak dla programisty, oprócz wsparcia jakie daje Maven, bardzo istotne są również narzędzia udostępniane przez środowisko deweloperskie. W przypadku projektów webowych w Javie jest to Eclipse WTP.

Problemem jest jednak skonfigurowanie WTP tak, aby prawidłowo działał w projekcie wykorzystującym Mavena. Nie znam tutaj dobrej, sprawdzającej się we wszystkich przypadkach metody. Stosuję następującą procedurę:

1. Tworzę Dynamic Web Project korzystając z kreatora w Eclipse.
2. Włączam obsługę Mavena.
3. Eksportuję zależności obsługiwane przez Maven jako J2EE Module Dependencies.

Utworzenie projektu

Korzystając z kreatora tworzę tzw. Dynamic Web Project z następującymi ustawieniami:
- Target Runtime: Apache Tomcat v6.0
- Dynamic Web Module: 2.5
- Java: 6.0
- Context Root: [jaki się chce]
- Content Directory: src/main/webapp
- Java Source Directory: src/main/java

Dwa ostatnie parametry wskazują, że będziemy stosować Mavenowy układ katalogów.

Włączenie Mavena

Włączam obsługę zależności przy pomocy Mavena (Enable Dependency Management) podając następujące ustawienia:
- Group Id: [jaki się chce]
- Artifact Id: [jaki się chce]
- Version: [jaką się chce]
- Packaging: war

Eksport zależności Mavena

Włączam eksport zależności obsługiwanych przez Mavena jako J2EE Module Dependencies.

Project Properties -> J2EE Module Dependencies -> zaznaczyć Maven Dependencies w JAR/MODULE

Konfiguracja JAX-WS w Maven 2

Do zrobienia są następujące rzeczy:
1. Konfiguracja kompilatora dla Javy 6.
2. Konfiguracja wsimport do generowania klas ze schemy (ponieważ stosuję metodologię top-down).
3. Konfiguracja zależności projektu.

Konfiguracja kompilatora

Rzecz opisana w wielu miejscach, więc pomijam.

Plug in dla wsimport

Pluginem obsługującym generowanie klas na podstawie WSDL jest jaxws-maven-plugin. Znajduje się w standardowym repozytorium Mavena, groupId=org.codehaus.mojo, artifactId=jaxws-maven-plugin, version=1.10.

Podczas konfigurowania tego plugina istotne jest zdecydowanie gdzie będzie umieszczony plik WSDL. Znalazłem dwa podejścia: katalog /src/wsdl i katalog /src/main/webapp/WEB-INF/wsdl. Pierwsze podejście jest bardziej eleganckie, ale wymaga kopiowania pliku WSDL do miejsca docelowego podczas budowania projektu. Drugie podejście jest prostsze i to zawsze stosuję.

Konfiguracja wsimport


<build>
<plugins>
...
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>jaxws-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>wsimport</goal>
</goals>
<configuration>
<packageName>[nazwa package]</packageName>
<wsdlDirectory>[ścieżka do WSDL]</wsdlDirectory>
<wsdlFiles>
<wsdlFile>[nazwa pliku WSDL]</wsdlFile>
</wsdlFiles>
</configuration>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>com.sun.xml.ws</groupId>
<artifactId>jaxws-tools</artifactId>
<version>2.1.4</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>


Lista zależności

Reference implementation JAX-WS wraz z kodem źródłowym znajduje się w standardowym repozytorium Maven, groupId=com.sun.xml.ws, artifactId=jaxws-rt, version=2.1.4.

Konfiguracja zależności projektu


<dependencies>
<dependency>
<groupId>com.sun.xml.ws</groupId>
<artifactId>jaxws-rt</artifactId>
<version>2.1.4</version>
</dependency>
</dependencies>


Uwaga, w dalszej części wpisu lista zależności będzie uzupełniona.

Błędy podczas uruchamiania wsimport

Gdy stosuje się domyślną konfigurację, trudno jest określić rodzaj i przyczynę błędu powstałego podczas uruchamiania wsimport przy pomocy plugina jaxws-maven-plugin. Jeżeli pliku WSDL nie ma, lub nie jest umieszczony w prawidłowym miejscu, komunikat jest jasny, lecz w przypadku błędów w samym pliku WSDL informacje wyświetlane w konsoli są niewystarczające.

Włączając opcję 'debug output' w oknie uruchamiania Mavena w Eclipse, lub stosując parametr -e linii polceceń Mavena można uzyskać więcej informacji, w szczególności stacktrace.

Ja miałem problem z Javą 5, która nie posiadała którejś klasy z pakietu javax.jws, przez co generowanie klas na podstawie WSDL kończyło się błędem. Niestety autorzy plug-ina łapiąc wyjątek ClassDefNotFoundException (w poniższym kodzie jest to zmienna e) nie raczyli wyświetlić pełnego stack trace:


} catch (Exception e) {
throw new MojoExecutionException( "Error executing: wsimport " + args );
}


Notabene po przełączeniu Eclipse na Javę 6 i powrocie do Javy 5 problem zniknął.

Innym razem chodziło o błędne podłączenie JRE zamiast JDK w Eclipse, przez co wsimport nie miał dostępu do tools.jar.

Podłączanie wygenerowanych klas jako źródeł projektu w Eclipse

Wygenerowane klasy standardowo lądują w katalogu /target/jaxws/wsimport/java/, który należy podłączyć jako dodatkowy katalog źródeł projektu w Eclipse.

Po podłączeniu okazuje się, że nie mamy podłączonych klas z pakietu javax.jws, czyli JSR-181. Niestety, w standardowym repozytorium Maven próżno tej biblioteki szukać (a przynajmniej mnie nie udało się znaleźć), więc trzeba do projektu dołączyć repozytorium download.java.net:


<repositories>
<repository>
<id>maven-repository.dev.java.net</id>
<url>http://download.java.net/maven/1/</url>
<layout>legacy</layout>
</repository>
</repositories>


Następnie trzeba dodać do projektu zależność od JSR-181:


<dependencies/>
...
<dependency>
<groupId>javax.jws</groupId>
<artifactId>jsr181-api</artifactId>
<version>1.0-MR1</version>
</dependency>
</dependencies>


Właściwie nie mam pojęcia dlaczego trzeba tę akurat bibliotekę podłączać ręcznie.

Utworzenie deskryptorów

Do uruchomienia web serwisu konieczne jest utworzenie następujących deskryptorów:
1. web.xml
2. sun-jaxws.xml

web.xml

Deployment Descriptor musi zawierać dwa elementy: listener wywoływany podczas rozpraszania aplikacji oraz servlet obsługujący zapytania otrzymywane przez web serwis. Wygląda to następująco:


<listener>
<listener-class>
com.sun.xml.ws.transport.http.servlet.WSServletContextListener
</listener-class>
</listener>

<servlet>
<servlet-name>WSServlet</servlet-name>
<servlet-class>
com.sun.xml.ws.transport.http.servlet.WSServlet
</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
<servlet-name>WSServlet</servlet-name>
<url-pattern>[względna ścieżka dla JAX-WS zaczynająca się znakiem /]</url-pattern>
</servlet-mapping>


sun-jaxws.xml

Deskryptor JAX-WS zawiera informacje o rozpraszanych web serwisach. Szczerze mówiąc nie mam pojęcia do czego jest w ogóle potrzebny, skoro używane są adnotacje.


<?xml version="1.0" encoding="UTF-8"?>
<endpoints xmlns="http://java.sun.com/xml/ns/jax-ws/ri/runtime" version="2.0">
<endpoint
name="[dowolna nazwa]"
implementation="[full-qualified name klasy implementującej web serwis]"
wsdl="[ścieżka do WSDL zaczynająca się od WEB-INF/wsdl/]"
url-pattern="[względna ścieżka dla JAX-WS zaczynająca się znakiem /]"
/>
</endpoints>


Implementacja web serwisu

Podczas implementowania web serwisu istotne jest odpowiednie użycie adnotacji. Wszystko jest dobrze opisane w JSR-181. Ponieważ stosujemy metodykę top-down, konieczne jest zastosowanie adnotacji @WebService z argumentem endpointInterface.

Oprócz argumentu endpointInterface konieczne jest określenie wartości następujących argumentów:
- serviceName (taki jak name w wsdl:service)
- portName (taki jak name w wsdl:port wewnątrz wsdl:service)
- targetNamespace (taki jak targetNamespace w nagłówku WSDL)

Ustawienie tych wartości jest potrzebne aby wygenerowane klasy ściśle odpowiadały definicjom w pliku WSDL. Jeżeli tego nie zrobimy, podczas rozpraszania aplikacji otrzymamy komunikat "Not a primary WSDL" (ze szczegółami, które łatwo pozwalają znaleźć przyczynę ewentualnego problemu).

Możliwe jest również takie skonfigurowanie aplikacji, aby JAX-WS generował własny WSDL na podstawie adnotacji. Wtedy w adnotacji @WebService klasy implementującej web serwis wystarczy tylko argument endpointInterface, a z sun-jaxws.xml należy usunąć parametr wsdl.

Testowanie web serwisu

Rozpraszanie aplikacji

Pierwsza rzecz to prawidłowe rozproszenie aplikacji. W większości przypadków, z którymi miałem do czynienia, komunikaty w logu były sensowne i szybko naprowadzały mnie na przyczynę problemów.

Oprócz komunikatów błędów w logu znaleźć można również ostrzeżenia. Nie wszystkie zdołałem dotąd pojąć. Przykładowo nie wiem dlaczego pojawia się takie ostrzeżenie:


The listener "com.sun.xml.ws.transport.http.servlet.WSServletContextListener" is
already configured for this context. The duplicate definition has been ignored.


Wyświetlanie listy web serwisów

W przypadku rozpraszania na własnej stacji roboczej pod Tomcatem sprawdzamy przy pomocy przeglądarki internetowej rezultat pod adresem http://localhost:8080/[nazwa aplikacji]/[względna ścieżka JAX-WS].

Wysyłanie testowych zapytań

Do wysyłania testowych zapytań można użyć Web Services Explorera dostępnego w Eclipse.

Brak komentarzy:

Prześlij komentarz