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