WstępTematem 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żenia1. 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 wykonania1. 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ęcia1. 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 2Maven 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 projektuKorzystają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 MavenaWłą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 MavenaWłą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 2Do 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 kompilatoraRzecz opisana w wielu miejscach, więc pomijam.
Plug in dla wsimportPluginem 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ściReference 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 wsimportGdy 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 EclipseWygenerowane 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ówDo uruchomienia web serwisu konieczne jest utworzenie następujących deskryptorów:
1. web.xml
2. sun-jaxws.xml
web.xmlDeployment 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.xmlDeskryptor 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 serwisuPodczas 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 serwisuRozpraszanie aplikacjiPierwsza 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ówW 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.