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.

Spis treści

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.

Grafika:jaxws-dictionary-konsola-glassfish-webservices.png

Wybieramy przycisk Test.

Grafika:jaxws-dictionary-tester.PNG

Wprowadzamy dowolne słowo, np. koniec i wciskamy przycisk translate, który odpowiada metodzie publicznej naszej usługi.

Grafika:jaxws-dictionary-tester-wynik.png

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.

Osobiste