Tworzenie usługi sieciowej z JAX-WS
Z Jacek Laskowski - Wiki Projektanta Java EE
Podczas konstruowania przykładu do artykułu SCAlenie (kompozyt) z Apache Tuscany i Apache Maven potrzebowałem kilka usług, które byłyby podstawą dla SCAlenia. W artykule pojawiły się jedynie usługi będące referencjami do klas Java, więc przykład mógł stracić na swojej wartości edukacyjnej. W tym artykule zamierzam nadrobić startę i przygotować grunt pod rozwój nowej wersji SCAlenia korzystającego z usługi sieciowej PolishEnglishDictionary z wykorzystaniem specyfikacji The Java API for XML-Based Web Services (JAX-WS).
Wersje oprogramowania w środowisku uruchomieniowym:
Zakłada się, że powyższe oprogramowanie jest zainstalowane i działa poprawnie.
Kompletny projekt jest dostępny jako jaxws-dictionary.zip.
Utworzenie projektu usługi sieciowej - PolishEnglishDictionary
Mamy kilka możliwości stworzenia aplikacji internetowej korzystając z NetBeans IDE, Eclipse IDE czy Apache Maven 2 (m2). W tym artykule stawiamy na m2 i potraktujemy graficzne środowiska programistyczne (IDE) jedynie jako narzędzia wspierające edycję składowych projektu.
Rozpoczynamy od stworzenia projektu aplikacji internetowej, która będzie reprezentowała usługę sieciową, przy pomocy m2. Projekt tworzymy w wybranym przez siebie katalogu, np. C:\.
$ mvn archetype:create -DarchetypeArtifactId=maven-archetype-webapp -DgroupId=pl.jaceklaskowski.jaxws.dictionary -DartifactId=jaxws-dictionary [INFO] Scanning for projects... [INFO] Searching repository for plugin with prefix: 'archetype'. [INFO] ---------------------------------------------------------------------------- [INFO] Building Maven Default Project [INFO] task-segment: [archetype:create] (aggregator-style) [INFO] ---------------------------------------------------------------------------- ... [INFO] Archetype created in dir: c:\jaxws-dictionary [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESSFUL [INFO] ------------------------------------------------------------------------ [INFO] Total time: 2 seconds [INFO] Finished at: Sun Jun 03 17:02:45 CEST 2007 [INFO] Final Memory: 5M/254M [INFO] ------------------------------------------------------------------------
Przechodzimy do katalogu jaxws-dictionary.
$ cd jaxws-dictionary
Kolejne polecenia wydawane będą właśnie z tego katalogu, który reprezentuje nasz projekt usługi sieciowej.
Wykonujemy polecenie mvn eclipse:eclipse, aby utworzyć pliku projektu do importu projektu do Eclipse.
$ mvn eclipse:eclipse [INFO] Scanning for projects... [INFO] Searching repository for plugin with prefix: 'eclipse'. [INFO] ---------------------------------------------------------------------------- [INFO] Building jaxws-dictionary Maven Webapp [INFO] task-segment: [eclipse:eclipse] [INFO] ---------------------------------------------------------------------------- ... [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESSFUL [INFO] ------------------------------------------------------------------------ [INFO] Total time: 1 second [INFO] Finished at: Sun Jun 03 17:12:09 CEST 2007 [INFO] Final Memory: 3M/254M [INFO] ------------------------------------------------------------------------
Komunikat BUILD SUCCESSFUL wskazuje na poprawnie wykonane polecenie. Uruchamiamy Eclipse IDE i importujemy projekt (File > Import > Existing Projects into Workspace).
UWAGA: Może się zdarzyć, że po imporcie pojawi się komunikat ostrzegawczy - Unbound classpath variable: M2_REPO/junit/junit/3.8.1/junit-3.8.1.jar in project jaxws-dictionary - co oznacza brak definicji zmiennej M2_REPO w wybranej przestrzeni roboczej. Mimo, że Eclipse zgłasza to jako komunikat ostrzegawczy, po dodaniu kolejnych zalezności w projekcie nie będzie możliwe tworzenie klas korzystających z nich z poziomu Eclipse. Naprawiamy błąd poprzez Window > Preferences... > Java > Build Path > Classpath Variables > New.... Dodajemy M2_REPO ze wskazaniem na lokalne repozytorium M2, potwierdzamy ponowne przebudowanie projektu i voila - projekt jest czysty, bez jakichkolwiek komunikatów ostrzegawczych, gotowy do dalszego rozwoju.
Modyfikujemy pom.xml o definicje zależności związane z bibliotekami JAX-WS i konfigurację wtyczki maven-compiler-plugin tak, aby projekt pracował z Java SE 5. Zależność od biblioteki JAX-WS jest w zasięgu provided, gdyż biblioteka jest częścią środowiska serwera aplikacyjnego Java EE 5 i nie musi stanowić części aplikacji internetowej.
<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.jaceklaskowski.jaxws.dictionary</groupId>
<artifactId>jaxws-dictionary</artifactId>
<packaging>war</packaging>
<version>1.0</version>
<name>Usługa sieciowa PolishEnglishDictionary z JAX-WS</name>
<url>http://www.JacekLaskowski.pl</url>
<build>
<finalName>${artifactId}</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.5</source>
<target>1.5</target>
</configuration>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>java.net repository</id>
<name>java.net Maven 1.x Repository</name>
<url>https://maven-repository.dev.java.net/nonav/repository/</url>
<layout>legacy</layout>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>com.sun.xml.ws</groupId>
<artifactId>jaxws-rt</artifactId>
<version>2.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.2</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
Po zmianie pom.xml ponownie generujemy pliki projektu dla wybranego środowiska programistycznego IDE.
$ mvn eclipse:eclipse [INFO] Scanning for projects... [INFO] Searching repository for plugin with prefix: 'eclipse'. [INFO] ---------------------------------------------------------------------------- [INFO] Building Usługa sieciowa PolishEnglishDictionary z JAX-WS [INFO] task-segment: [eclipse:eclipse] [INFO] ---------------------------------------------------------------------------- ... [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESSFUL [INFO] ------------------------------------------------------------------------ [INFO] Total time: 1 second [INFO] Finished at: Sun Jun 03 17:16:55 CEST 2007 [INFO] Final Memory: 3M/254M [INFO] ------------------------------------------------------------------------
Z BUILD SUCCESSFUL możemy być pewni, że wszystko jest poprawnie zestawione i możemy przejść do kolejnego kroku.
Odświeżamy projekt w wybranym środowisku IDE i rozpoczynamy tworzenie składowych usługi, np. w Eclipse IDE wciskamy F5 mając zaznaczony projekt jaxws-dictionary.
Utworzenie klasy usługi - pl.jaceklaskowski.jaxws.dictionary.PolishEnglishDictionary
Tworzymy klasę-implementację usługi PolishEnglishDictionary.
package pl.jaceklaskowski.jaxws.dictionary;
import javax.jws.WebMethod;
import javax.jws.WebService;
@WebService
public class PolishEnglishDictionary {
@WebMethod
public String translate(String word) {
return "en_" + word + "(web service)";
}
}
Poza adnotacjami @WebService oraz @WebMethod klasa PolishEnglishDictionary przedstawia się jak typowa klasa w Javie. Zadaniem adnotacji @WebService jest wskazanie środowisku uruchomieniowemu - serwerowi aplikacyjnemu Java EE 5, że oznaczona klasa jest usługą sieciową, a dostępne operacje są oznaczone przez adnotację @WebMethod. W naszym przykładzie jedyną dostępną operacją usługi jest translate.
Plik klasy - PolishEnglishDictionary.java - umieszczamy w katalogu src/main/java/pl/jaceklaskowski/jaxws/dictionary.
Modyfikacja deskryptora aplikacji internetowej - /WEB-INF/web.xml
Podczas tworzenia projektu aplikacji internetowej przy pomocy M2 został stworzony deskryptor aplikacji internetowej - web.xml, który dostępny jest w katalogu src/main/webapp/WEB-INF. Serwer aplikacyjny rozpoznaje usługi sieciowe tworzone zgodnie ze specyfikacją JAX-WS tylko, jeśli projekt oznaczony jest jako zgodny ze specyfikacją Java Servlet w wersji 2.5. Informacja jest zapisana w deskryptorze, który zakładany jest przez m2 w wersji 2.3. Zmieniamy zawartość web.xml na poniższą.
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5"> <display-name>PolishEnglishDictionary Service</display-name> </web-app>
Plik deskryptora - web.xml - umieszczamy w katalogu src/main/webapp/WEB-INF.
Uruchomienie usługi
Zbudowanie usługi za pomocą M2
Projekt zarządzany jest przez M2 i właśnie z niego skorzystamy do zbudowania usługi przed jej faktycznym uruchomieniem na GlassFish. Wykonujemy polecenie mvn package.
$ mvn package [INFO] Scanning for projects... [INFO] ---------------------------------------------------------------------------- [INFO] Building Usługa sieciowa Dictionary z JAX-WS [INFO] task-segment: [package] [INFO] ---------------------------------------------------------------------------- ... [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESSFUL [INFO] ------------------------------------------------------------------------ [INFO] Total time: 2 seconds [INFO] Finished at: Sun Jun 03 20:53:30 CEST 2007 [INFO] Final Memory: 4M/254M [INFO] ------------------------------------------------------------------------
W katalogu target znajduje się wersja instalacyjna aplikacji internetowej - jaxws-dictionary.war.
Instalacja usługi w GlassFish
Instalację usługi rozpoczniemy od uruchomienia serwera aplikacji GlassFish poleceniem ./bin/asadmin.bat start-domain domain1. W zależności od konfiguracji serwera będziemy zmuszeni do przejścia do katalogu domowego serwera GlassFish lub po prostu wykonania polecenia asadmin.bat z parametrami, jeśli katalog skryptów jest w zmiennej PATH.
$ asadmin.bat start-domain domain1 Starting Domain domain1, please wait. Log redirected to c:\apps\glassfish\domains\domain1\logs\server.log. Redirecting output to C:/apps/glassfish/domains/domain1/logs/server.log Domain domain1 is ready to receive client requests. Additional services are being started in background. Domain [domain1] is running [Sun Java System Application Server 9.1 (build b49-beta3)] with its configuration and logs at: [c:\apps\glassfish\domains]. Admin Console is available at [http://localhost:4848]. Use the same port [4848] for "asadmin" commands. User web applications are available at these URLs: [http://localhost:8080 https://localhost:8181 ]. Following web-contexts are available: [/web1 /__wstx-services /glassfish-postgresql jaxws-dictionary ]. Standard JMX Clients (like JConsole) can connect to JMXServiceURL: [service:jmx:rmi:///jndi/rmi://dev:8686/jmxrmi] for domain management purposes. Domain listens on at least following ports for connections: [8080 8181 4848 3700 3820 3920 8686 ]. Domain does not support application server clusters and other standalone instances.
Istnieje kilka innych możliwości instalacji aplikacji zarządzanej przez M2, jak chociażby skorzystanie z wtyczki M2 dla GlassFish autorstwa Andrzeja Śliwy lub konfiguracji wtyczki maven-war-plugin o element webappDirectory, który wskaże na odpowiedni katalog w GlassFish. Tym razem skorzystamy z polecenia asadmin.bat deploy target/jaxws-dictionary.war (UWAGA: wersja skrócona dostępna jedynie dla osób, które zdefiniowały zmienną PATH na katalog GLASSFISH_HOME/bin).
$ asadmin.bat deploy target/jaxws-dictionary.war Command deploy executed successfully.
Śledząc wykonanie GlassFish podczas uruchomienia polecenia asadmin deploy możemy zauważyć następujące wpisy, które gwarantują poprawną instalację usługi.
$ tail -f domains/domain1/logs/server.log ... [#|2007-06-03T21:22:33.406+0200|INFO|sun-appserver9.1|javax.enterprise.system.container.ejb|_ThreadID=17;_ThreadName=Thread-31;|wsgen successful|#] [#|2007-06-03T21:22:33.812+0200|INFO|sun-appserver9.1|javax.enterprise.system.tools.deployment|_ThreadID=17; _ThreadName=Thread-31;Servlet;PolishEnglishDictionary;http://127.0.0.1:8080/jaxws-dictionary/PolishEnglishDictionaryService;| DPL5306:Servlet Web Service Endpoint [PolishEnglishDictionary] listening at address [http://127.0.0.1:8080/jaxws-dictionary/PolishEnglishDictionaryService]|#] [#|2007-06-03T21:22:34.046+0200|INFO|sun-appserver9.1|javax.enterprise.system.tools.deployment|_ThreadID=17;_ThreadName=Thread-31;| deployed with moduleid = jaxws-dictionary|#]
Warto odnotować adres usługi http://127.0.0.1:8080/jaxws-dictionary/PolishEnglishDictionaryService. Jest to jeden ze sposobów dotarcia do informacji o adresie usługi. Kolejnym jest skorzystanie z konsoli administracyjnej GlassFish, która domyślnie dostępna jest pod adresem http://localhost:4848.
Uruchomienie usługi za pomocą konsoli administracyjnej GlassFish
Uruchomienie testowe usługi bez konieczności tworzenia klienta możemy wykonać przy pomocy konsoli administracyjnej GlassFish.
Otwieramy konsolę GlassFish dostępną pod adresem http://localhost:4848. Wybieramy węzeł Web Services, gdzie wybieramy węzeł PolishEnglishDictionary.
Wybieramy przycisk Test.
Wprowadzamy dowolne słowo, np. koniec i wciskamy przycisk translate, który odpowiada metodzie publicznej naszej usługi.
W wyniku, w sekcji Method returned, otrzymujemy wprowadzone słowo rozpoczynające się prefiksem en_ oraz zakończone (web service), co potwierdza poprawne wykonanie usługi (implementacja metody translate to właśnie "opakowanie" słowa w en_ oraz (web service)).
I to na tyle! W ten sposób udało nam się zrealizować usługę PolishEnglishDictionary, którą wykorzystamy do rozwinięcia SCAlenia z artykułu SCAlenie (kompozyt) z Apache Tuscany i Apache Maven.


