Tworzenie usługi sieciowej z JAX-WS, Apache Geronimo 2 i NetBeans 6

Z Jacek Laskowski - Wiki Projektanta Java EE

W ostatnim artykule dotyczącym tworzenia usług sieciowych z wykorzystaniem JAX-WS - Tworzenie usługi sieciowej z JAX-WS korzystałem z pomocy serwera aplikacji GlassFish 2 oraz narzędzi Apache Maven 2 i Eclipse 3.3. W przykładzie użyłem obowiązkowych adnotacji @WebService oraz @WebMethod, odpowiednio do określenia klasy jako usługi sieciowej oraz metod usługi. Tym razem skorzystam ze wsparcia NetBeans 6 do utworzenia usługi sieciowej, którą następnie uruchomię na wersji rozwojowej serwera Apache Geronimo 2.1. Ciekawostką Geronimo będzie możliwość uruchomienia usługi w ramach aplikacji internetowej bez deskryptora web.xml. Na zakończenie utworzę klienta usługi jako samodzielną aplikację.

Spis treści

Oprogramowanie

Środowisko składa się z następujących narzędzi:

Zakłada się, że powyższe oprogramowanie jest zainstalowane i działa poprawnie. Instalacja oprogramowania sprowadza się do pobrania i rozpakowania paczek w wybranym katalogu.

Kompletne projekty katalog-jaxws-geronimo oraz katalog-jaxws-geronimo-klient do zaimportowania do NetBeans 6 dostępne są do pobrania jako katalog-jaxws-geronimo.zip.

Utworzenie usługi Katalog w NetBeans

Utworzenie projektu aplikacji internetowej - katalog-jaxws-geronimo

Rozpoczynamy od utworzenia projektu aplikacji internetowej katalog-jaxws-geronimo. Korzystamy z kombinacji klawiszy Ctrl+Shift+N, gdzie wybieramy Web > Web Application.

Grafika:katalog-jaxws-geronimo-newwebapp.PNG

Wciskamy przycisk Next >.

W okienku dialogowym New Web Application w polu Project Name wpisujemy katalog-jaxws-geronimo i wybieramy miejsce utworzenia aplikacji - pole Project Location, np. C:

Grafika:katalog-jaxws-geronimo-newwebapp-nameandlocation.PNG

Wciskamy przycisk Finish.

Klasa usługi sieciowej Katalog - pl.jaceklaskowski.katalog.ws.KatalogWS

Przechodzimy do utworzenia usługi sieciowej Katalog. Mając zaznaczony nowoutworzony projekt katalog-jaxws-geronimo wciskamy Ctrl+N, gdzie wybieramy Web Services > Web Service.

Grafika:katalog-jaxws-geronimo-newwebservice.PNG

Wciskamy przycisk Next >.

W oknie dialogowym New Web Service podajemy następujące parametry:

  • Web Service Name: KatalogWS
  • Package: pl.jaceklaskowski.katalog.ws
Grafika:katalog-jaxws-geronimo-newwebservice-nameandlocation.PNG

Zatwierdzamy przyciskiem Finish.

Dodajemy nową metodę usługi KatalogWS wciskając przycisk Add Operation...

Grafika:katalog-jaxws-geronimo-newwebservice-newoperation.PNG

W oknie dialogowym Add Operation... podajemy następujące parametry metody sieciowej:

  • Name: iloscPozycji
  • Return type: int

oraz w zakładce Parameters po wciśnięciu przycisku Add:

  • Name: nazwaKatalogu
Grafika:katalog-jaxws-geronimo-newwebservice-addoperation.PNG

Zatwierdzamy przyciskiem OK.

Nasza usługa wygląda następująco w NetBeans w trybie Design.

Grafika:katalog-jaxws-geronimo-newwebservice-iloscpozycji.PNG

Zmieniamy tryb prezentacji na Source (lewy górny róg edytora usługi) i zmieniamy źródło usługi na następujące:

package pl.jaceklaskowski.katalog.ws;

import java.util.logging.Logger;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebService;

@WebService(serviceName = "Katalog")
public class KatalogWS {

    @WebMethod(operationName = "iloscPozycji")
    public int iloscPozycji(            
            @WebParam(name = "nazwaKatalogu") String nazwaKatalogu) {
        return nazwaKatalogu == null ? null : nazwaKatalogu.length();
    }

    @PostConstruct
    void init() {
        Logger.getLogger("katalog").info("Usluga sieciowa Katalog utworzona");
    }

    @PreDestroy
    void destroy() {
        Logger.getLogger("katalog").info("Usluga sieciowa Katalog niszczona");
    }
}

W ten sposób zmieniamy nazwę usługi na Katalog poprzez skorzystanie z atrybutu serviceName adnotacji @WebService. Dodaliśmy również dwie metody zwrotne - pierwsza metoda init() z adnotacją @PostConstruct wykonywana po uruchomieniu usługi przez serwer aplikacyjny a przed obsługą pierwszego żądania klienta oraz druga destroy() z adnotacją @PreDestroy wykonywaną na moment przed zniszczeniem usługi przez serwer (np. podczas jego zatrzymania).

Zapisujemy zmiany wciskając Ctrl+S.

Usunięcie deskryptora aplikacji internetowej - web.xml

Usuwamy deskryptor aplikacji internetowej web.xml z Web Pages > WEB-INF.

Grafika:katalog-jaxws-geronimo-webxml.PNG

Opcjonalnie możemy również usunąć stronę index.jsp, bądź zmodyfikować tak, aby przekierowywała na adres usługi sieciowej Katalog.

<jsp:forward page="Katalog" />

Utworzenie planu wdrożenia dla Geronimo - geronimo-web.xml

W katalogu Web Pages > WEB-INF tworzymy plik geronimo-web.xml. Jest to plik definiujący nazwę dla naszej aplikacji, która upraszcza późniejsze nią zarządzanie z poziomu Geronimo. Dodatkowo jest to sposób na przypisanie nazwy kontekstu aplikacji na /katalog (w przeciwnym przypadku byłaby katalog-jaxws-geronimo - nazwa odpowiadająca nazwie pliku war bez rozszerzenia).

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://geronimo.apache.org/xml/ns/j2ee/web-2.0.1">
    <dep:environment xmlns:dep="http://geronimo.apache.org/xml/ns/deployment-1.2">
        <dep:moduleId>
            <dep:groupId>pl.jaceklaskowski.katalog</dep:groupId>
            <dep:artifactId>katalog-webservice</dep:artifactId>
            <dep:version>1.0</dep:version>
            <dep:type>war</dep:type>
        </dep:moduleId>
    </dep:environment>
    <context-root>/katalog</context-root>
</web-app>

Uruchomienie usługi Katalog na Geronimo

Na razie nie istnieje mechanizm w NetBeans, za pomocą którego możnaby uruchomić aplikację webową bezpośrednio w Geronimo. Posiłkujemy się obejściem, które polega na zbudowaniu aplikacji w NetBeans i skorzystanie z konsoli Geronimo bądź polecenia deploy z poziomu linii poleceń do jej wdrożenia (instalacji).

Skorzystamy z polecenia deploy.

Uruchomienie Geronimo

Uruchomienie aplikacji webowej zaczynamy od jej wdrożenia na serwerze Geronimo. Uruchamiamy Geronimo poleceniem ./bin/geronimo.sh run -vv. Parametr -vv (veryverbose) instruuje Geronimo, aby wyświetlał komunikaty niższego poziomu, które są niezwykle przydatne podczas tworzenia aplikacji. Zalecane jest, aby nie stosować tego parametru podczas uruchomienia produkcyjnego, właśnie ze względu na ilość wypisywanych komunikatów.

Przechodzimy do katalogu Geronimo i wydajemy polecenie ./bin/geronimo.sh run -vv (na systemie Windows będzie to odpowiednio geronimo.bat).

jlaskowski@dev /cygdrive/c/geronimo
$ ./bin/geronimo.sh run -vv
Using GERONIMO_BASE:   c:\geronimo
Using GERONIMO_HOME:   c:\geronimo
Using GERONIMO_TMPDIR: c:\geronimo\var\temp
Using JRE_HOME:        c:\apps\java5\jre
15:57:09,796 DEBUG [BasicKernel] Starting boot
15:57:09,968 DEBUG [GBeanInstanceState] GBeanInstanceState for: geronimo/boot/none/car?role=kernel State changed from stopped to starting
15:57:09,968 DEBUG [GBeanInstanceState] GBeanInstanceState for: geronimo/boot/none/car?role=kernel State changed from starting to running
15:57:09,968 DEBUG [BasicKernel] Booted
15:57:10,031 DEBUG [AbstractRepository] Repository root is C:\geronimo\repository
...
15:57:50,562 INFO  [startup] Deployed Application(path=C:\.m2\org\apache\geronimo\applications\geronimo-mejb\2.1-SNAPSHOT\geronimo-mejb-2.1-SNAPSHOT.jar)
Geronimo startup complete

Konsola administracyjna znajduje się domyślnie pod adresem http://localhost:8080/console. Można z niej również skorzystać do instalacji.

Zbudowanie aplikacji internetowej w NetBeans - katalog-jaxws-geronimo.war

Z poziomu NetBeans wydajemy polecenie Clean and Build.

Grafika:katalog-jaxws-geronimo-cleanandbuild.PNG

W ten sposób, w katalogu C:\katalog-jaxws-geronimo\dist NetBeans umieści naszą aplikację - katalog-jaxws-geronimo.war.

init:
deps-clean:
do-clean:
check-clean:
clean:
init:
deps-module-jar:
deps-ear-jar:
deps-jar:
Created dir: C:\katalog-jaxws-geronimo\build\web\WEB-INF\classes
Created dir: C:\katalog-jaxws-geronimo\build\web\META-INF
Copying 1 file to C:\katalog-jaxws-geronimo\build\web\META-INF
Copying 3 files to C:\katalog-jaxws-geronimo\build\web
Copied 1 empty directory to 1 empty directory under C:\katalog-jaxws-geronimo\build\web
library-inclusion-in-archive:
library-inclusion-in-manifest:
Compiling 1 source file to C:\katalog-jaxws-geronimo\build\web\WEB-INF\classes
compile:
compile-jsps:
Created dir: C:\katalog-jaxws-geronimo\dist
Building jar: C:\katalog-jaxws-geronimo\dist\katalog-jaxws-geronimo.war
do-dist:
dist:
BUILD SUCCESSFUL (total time: 0 seconds)

Wdrożenie aplikacji webowej z usługą sieciową Katalog

Do wdrożenia aplikacji internetowej skorzystamy z polecenia deploy. Przechodzimy do katalogu domowego Geronimo i wydajemy polecenie ./bin/deploy.sh -u system -p manager deploy "C:\katalog-jaxws-geronimo\dist\katalog-jaxws-geronimo.war"

$ ./bin/deploy.sh -u system -p manager deploy "C:\katalog-jaxws-geronimo\dist\katalog-jaxws-geronimo.war"
Using GERONIMO_BASE:   c:\geronimo
Using GERONIMO_HOME:   c:\geronimo
Using GERONIMO_TMPDIR: c:\geronimo\var\temp
Using JRE_HOME:        c:\apps\java5\jre
    Deployed pl.jaceklaskowski.katalog/katalog-webservice/1.0/war @
    /katalog

Podczas rozmieszczenia aplikacji na konsoli Geronimo pojawią się następujące komunikaty:

16:13:11,093 INFO  [JAXWSServiceBuilder] Configuring JAX-WS Web Service: pl.jaceklaskowski.katalog.ws.KatalogWS at /katalog/Katalog
16:13:11,453 INFO  [BusApplicationContext] Refreshing org.apache.cxf.bus.spring.BusApplicationContext@a92a55: display name 
[org.apache.cxf.bus.spring.BusApplicationContext@a92a55]; startup date [Sun Nov 18 16:13:11 CET 2007]; root of context hierarchy
16:13:11,546 INFO  [XmlBeanDefinitionReader] Loading XML bean definitions from class path resource [META-INF/cxf/cxf.xml]
16:13:12,031 INFO  [XmlBeanDefinitionReader] Loading XML bean definitions from class path resource [META-INF/cxf/cxf-extension-geronimo.xml]
16:13:12,078 INFO  [DefaultListableBeanFactory] Overriding bean definition for bean 'org.apache.cxf.resource.ResourceManager': 
replacing [Root bean: class [org.apache.cxf.bus.resource.ResourceManagerImpl]; 
scope=singleton; abstract=false; lazyInit=false; autowireCandidate=true; autowireMode=0; dependencyCheck=0; factoryBeanName=null; factoryMethodName=null; initMethod
Name=null; destroyMethodName=null; defined in class path resource [META-INF/cxf/cxf.xml]] 
with [Root bean: class [org.apache.geronimo.cxf.ResourceManager]; scope=singleton; abstract=false; lazyInit=false;
autowireCandidate=true; autowireMode=0; dependencyCheck=0; factoryBeanName=null; factoryMethodName=null; initMethodName=null;
destroyMethodName=null; defined in class path resource [META-INF/cxf/cxf-extension-geronimo.xml]]
16:13:12,078 INFO  [XmlBeanDefinitionReader] Loading XML bean definitions from class path resource [META-INF/cxf/cxf-extension-soap.xml]
16:13:12,109 INFO  [XmlBeanDefinitionReader] Loading XML bean definitions from class path resource [META-INF/cxf/cxf-extension-xml.xml]
16:13:12,171 INFO  [XmlBeanDefinitionReader] Loading XML bean definitions from class path resource [META-INF/cxf/cxf-extension-http.xml]
16:13:12,203 INFO  [BusApplicationContext] Bean factory for application context [org.apache.cxf.bus.spring.BusApplicationContext@a92a55]:
org.springframework.beans.factory.support.DefaultListableBeanFactory@87fa6d
16:13:12,328 INFO  [BusApplicationContext] Bean 'org.apache.cxf.bus.spring.Jsr250BeanPostProcessor' 
is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
16:13:12,328 INFO  [BusApplicationContext] Bean 'org.apache.cxf.bus.spring.BusExtensionPostProcessor' 
is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
16:13:12,328 INFO  [DefaultListableBeanFactory] Pre-instantiating singletons in 
org.springframework.beans.factory.support.DefaultListableBeanFactory@87fa6d: defining beans [...]; root of factory hierarchy
2007-11-18 16:13:13 org.apache.cxf.service.factory.ReflectionServiceFactoryBean buildServiceFromClass
INFO: Creating Service {http://ws.katalog.jaceklaskowski.pl/}Katalog from class pl.jaceklaskowski.katalog.ws.KatalogWS
2007-11-18 16:13:14 pl.jaceklaskowski.katalog.ws.KatalogWS init
INFO: Usluga sieciowa Katalog utworzona

Uwagę należy skupić na dwóch komunikatach. Pierwszy z nich wskazuje na adres naszej usługi sieciowej:

16:13:11,093 INFO  [JAXWSServiceBuilder] Configuring JAX-WS Web Service: pl.jaceklaskowski.katalog.ws.KatalogWS at /katalog/Katalog

W tym przypadku będzie to http://localhost:8080/katalog/Katalog oraz komunikat po uruchomieniu metody zwrotnej @PostCreate.

INFO: Usluga sieciowa Katalog utworzona

Oba komunikaty gwarantują poprawne uruchomienie usługi sieciowej.

Wejście na adres usługi http://localhost:8080/katalog/Katalog spowoduje wyświetlenie następującego komunikatu:

Grafika:katalog-jaxws-geronimo-thisiskatalogwebservice.PNG

Utworzenie klienta usługi sieciowej - Klient

Utworzenie projektu klienta - katalog-jaxws-geronimo-klient

Tworzymy projekt klienta wciskając Ctrl+Shift+N. Z okienka dialogowego New Project wybieramy Java > Java Application.

Grafika:katalog-jaxws-geronimo-javaapplication.PNG

Wciskamy przycisk Next >.

W oknie dialogowym New Java Application wpisujemy następujące dane:

  • Project Name: katalog-jaxws-geronimo-klient
  • Create Main Class: pl.jaceklaskowski.katalog.Klient
Grafika:katalog-jaxws-geronimo-javaapplication-nameandlocation.png

Wciskamy przycisk Finish.

Utworzenie referencji usługi sieciowej

Wciskamy Ctrl+N i z okna dialogowego New File wybieramy Web Services > Web Service Client.

Grafika:katalog-jaxws-geronimo-newwebserviceclient.PNG

Wciskamy przycisk Next >.

Zaznaczamy opcję WSDL URL, gdzie wpisujemy adres pliku WSDL dla naszej usługi Katalog na Geronimo - http://localhost:8080/katalog/Katalog?wsdl. Z menu Package wybieramy pakiet pl.jaceklaskowski.katalog.

Grafika:katalog-jaxws-geronimo-newwebserviceclient-wsdlandclientlocation.PNG

Zatwierdzamy przyciskiem Finish.

Poprawne wykonanie jest reprezentowane przez nowy węzeł w NetBeans - Web Service References.

Grafika:katalog-jaxws-geronimo-webservicereferences.PNG

Dzięki tej funkcjonalności możemy skorzystać z menu Web Service Client Resources > Call Web Service Operation... podczas edycji źródła klasy pl.jaceklaskowski.katalog.Klient. Więcej o tym w artykule Getting Started with JAX-WS Web Service Clients in NetBeans IDE 6.0 lub End-to-End Web Service Creation and Consumption in NetBeans IDE 6.0.

Klasa klienta usługi - pl.jaceklaskowski.katalog.Klient

Ostatecznie klasa klienta Klient powinna wyglądać następująco:

package pl.jaceklaskowski.katalog;

public class Klient {

    public static void main(String[] args) {

        try {
            Katalog service = new Katalog();
            KatalogWS port = service.getKatalogWSPort();
            String nazwaKatalogu = "NazwaPewnegoKatalogu";
            if (args.length > 0) {
                nazwaKatalogu = args[0];
            }
            int result = port.iloscPozycji(nazwaKatalogu);
            System.out.println("Wynik = " + result);
        } catch (Exception ex) {
            ex.printStackTrace();
        }

    }
}

Uruchomienie klienta

Wykonanie klienta poprzez wciśnięcie F6 kończy się komunikatami:

init:
deps-jar:
wsimport-init:
wsimport-client-check-Katalog:
wsimport-client-Katalog:
wsimport-client-generate:
wsimport-client-compile:
Compiling 1 source file to C:\katalog-jaxws-geronimo-klient\build\classes
compile:
run:
Wynik = 20
BUILD SUCCESSFUL (total time: 2 seconds)

, gdzie linia Wynik = 20 jest poprawnym wykonaniem usługi.

Osobiste