Tak się nieszczęśliwie złożyło ostatnimi czasy, że musiałem zawiesić kilka własnych projektów na rzecz pracy w Urzędzie. Myślałem, że na blogu objawi się to małą ilością rozbudowanych opisów nowych zabawek, ale na całe szczęście zawiodłem się. W ręce wasze oddaję opis prostej aplikacji JEE5 napisanej z wykorzystaniem Web Services. Klientem serwisu jest tu zwykła aplikacja J5SE bez kontenera.

Co chcemy zrobić

Celem tego mini projektu jest stworzenie kalkulatora w postaci ziarna EJB3 udostępnianego jako Web Service. Klientem będzie „zwykła” aplikacja J5SE.

Dlaczego tak?

O ile pierwszy cel jest oczywisty to można sobie zadać pytanie po co pisać klienta w postaci SE skoro można użyć mechanizmu Application Client i uruchamiać klienta jako JNLP? Wyobraźmy sobie, że dysponujemy dwoma rozbudowanymi rejestrami. Pierwszy z nich zawiera dane naszych klientów. Drugi dane dotyczące ubezpieczeń naszych klientów. Pierwszy z rejestrów udostępnia tylko podstawowe dane o klientach i nie może zawierać żadnych innych informacji. Jeżeli chcielibyśmy stworzyć klienta Application Client działającego na serwerze z pierwszym spisem to uzyska on informacje o drugim rejestrze. Jeżeli uruchomimy go na serwerze z drugim rejestrem to dostajemy rozwiązanie nieelastyczne. Dlaczego? Dołóżmy zatem trzeci serwis, który korzysta z danych pierwszego serwisu i serwis pierwszy nie może nic wiedzieć o trzecim, a trzeci nie może nic wiedzieć o drugim. Niech trzeci serwis zawiera dane o na przykład o

kredytach klientów. Poniżej schemat jak to ma wyglądać.

Rysunek 1. zależności pomiędzy aplikacjami.

Można przecież napisać odpowiedniego klienta i postawić na osobnym serwerze. Ok można, nawet rozwiązał by się problem aktualizacji, która następowała by w jednym konkretnym miejscu. Jednak nadal pozostaje problem z obciążeniem. Docelowa aplikacja będzie miała około 50 tyś użytkowników, a zatem ruch pomiędzy poszczególnymi serwerami będzie ogromny. Dodatkowo część danych będzie przesyłana dwukrotnie. Najpierw z jednego rejestru na serwer z aplikacją kliencką, a następnie do użytkownika końcowego.

Lepszym rozwiązaniem jest zwykła aplikacjia SE. Po pierwsze spada ruch na serwerach, po drugie jest mniej punktów w których można naciąć się na błędy, po trzecie aplikacja SE odciąża nasz sprzęt, bo obróbka i scalanie danych następuje u użyszkodnika, wreszcie po czwarte aktualizacje można zrobić przez OSGi i nie ma problemu.

Początek, czyli projekt

Na początek przyjrzymy się temu jak będzie podzielona nasza aplikacja. Należy wyróżnić dwie główne części aplikację właściwą, działającą na serwerze oraz aplikację kliencką, która będzie przyjmować dane i wyświetlać wyniki.

W rzeczywistości Potrzebujemy trzech aplikacji. Dodatkową aplikacją jest wyciągnięta wspólna część API za pomocą którego będziemy programować część biznesową. Od tej też części zaczniemy tworzenie aplikacji.

Kalkulator-JavaEE-API

Jako, że mamy aplikację złożoną z kilku elementów to potrzebujemy ją jakoś zarządzać. Maven2 i wszystko jasne. Ok, tworzymy aplikację Kalkulator-JavaEE-API

Listing 1. Tworzenie Kalkulator-JavaEE-API w mavenie

>mvn archetype:create -DarchrtypeGroupId=org.apache.maven.archetypes /

 -DgroupId=eu.runelord.kalkulator.api -DartifactId=Kalkulator-JavaEE-API

[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] BUILD SUCCESSFUL [INFO]

------------------------------------------------------------------------

[INFO] Total time: 4 seconds [INFO] Finished at: Sat Dec 08 18:54:18 CET

2007 [INFO] Final Memory: 4M/8M [INFO]

------------------------------------------------------------------------

Dodajmy nasz projekt do Eclipsa i przystąpmy do definiowania interfejsów.

Na początek zdefiniujmy interfejs Kalkulator:

Listing 2. Interfejs Kalkulator


<span class="keywordStyle">package</span> <span class="packageNameStyle">eu.runelord.kalkulator.api</span>;

<span class="keywordStyle">public</span> <span class="keywordStyle">interface</span> <span class="nonPrimitiveTypeStyle">Kalkulator</span> {

    <span class="keywordStyle">public</span> <span class="nonPrimitiveTypeStyle">Integer</span> <span class="methodStyle">dodaj</span>(<span class="nonPrimitiveTypeStyle">Integer</span> <span class="variableStyle">a</span>, <span class="nonPrimitiveTypeStyle">Integer</span> <span class="variableStyle">b</span>) <span class="keywordStyle">throws</span> <span class="nonPrimitiveTypeStyle">BladWywolania</span>;

    <span class="keywordStyle">public</span> <span class="nonPrimitiveTypeStyle">Integer</span> <span class="methodStyle">odejmij</span>(<span class="nonPrimitiveTypeStyle">Integer</span> <span class="variableStyle">a</span>, <span class="nonPrimitiveTypeStyle">Integer</span> <span class="variableStyle">b</span>) <span class="keywordStyle">throws</span> <span class="nonPrimitiveTypeStyle">BladWywolania</span>;

    <span class="keywordStyle">public</span> <span class="nonPrimitiveTypeStyle">Integer</span> <span class="methodStyle">pomnoz</span>(<span class="nonPrimitiveTypeStyle">Integer</span> <span class="variableStyle">a</span>, <span class="nonPrimitiveTypeStyle">Integer</span> <span class="variableStyle">b</span>) <span class="keywordStyle">throws</span> <span class="nonPrimitiveTypeStyle">BladWywolania</span>;

    <span class="keywordStyle">public</span> <span class="nonPrimitiveTypeStyle">Integer</span> <span class="methodStyle">podziel</span>(<span class="nonPrimitiveTypeStyle">Integer</span> <span class="variableStyle">a</span>, <span class="nonPrimitiveTypeStyle">Integer</span> <span class="variableStyle">b</span>) <span class="keywordStyle">throws</span> <span class="nonPrimitiveTypeStyle">BladWywolania</span>;

}

Potrzebujemy jeszcze ogólnej klasy BladWywolania, która będzie sygnalizowała nam problemu.

Listing 3. Klasa BladWywolania


<span class="keywordStyle">package</span> <span class="packageNameStyle">eu.runelord.kalkulator.api</span>;

<span class="keywordStyle">public</span> <span class="keywordStyle">class</span> <span class="nonPrimitiveTypeStyle">BladWywolania</span> <span class="keywordStyle">extends</span> <span class="nonPrimitiveTypeStyle">Exception</span> {

    <span class="keywordStyle">private</span> <span class="keywordStyle">static</span> <span class="keywordStyle">final</span> <span class="nonPrimitiveTypeStyle">String</span> <span class="variableStyle">message</span> = <span class="doubleQuoteStyle">"Nastąpił błąd wywołania!"</span>;

    <span class="keywordStyle">public</span> <span class="constructorStyle">BladWywolania</span>() {

        <span class="keywordStyle">super</span>();

    }

    <span class="keywordStyle">public</span> <span class="constructorStyle">BladWywolania</span>(<span class="nonPrimitiveTypeStyle">String</span> <span class="variableStyle">arg0</span>, <span class="nonPrimitiveTypeStyle">Throwable</span> <span class="variableStyle">arg1</span>) {

        <span class="keywordStyle">super</span>(<span class="variableStyle">message</span>, <span class="variableStyle">arg1</span>);

    }

    <span class="keywordStyle">public</span> <span class="constructorStyle">BladWywolania</span>(<span class="nonPrimitiveTypeStyle">String</span> <span class="variableStyle">arg0</span>) {

        <span class="keywordStyle">super</span>(<span class="variableStyle">message</span>);

    }

    <span class="keywordStyle">public</span> <span class="constructorStyle">BladWywolania</span>(<span class="nonPrimitiveTypeStyle">Throwable</span> <span class="variableStyle">arg0</span>) {

        <span class="keywordStyle">super</span>(<span class="variableStyle">arg0</span>);

    }

}

Ok. Mamy już nasze gotowe API. Teraz wystarczy je tylko skompilować i zainstalować w lokalnym repo.

Listing 4. Instalacja Kalkulator-JavaEE-API w mavenie

>mvn clean compile jar:jar install

[INFO] Scanning for projects...

[INFO] Searching repository for plugin with prefix: 'jar'.

[INFO] -------------------------------------------------------------------------

...

-------------------------------------------------------

 T E S T S

-------------------------------------------------------

Running eu.runelord.kalkulator.api.AppTest

Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.047 sec

...

[INFO] ------------------------------------------------------------------------

[INFO] BUILD SUCCESSFUL

[INFO] ------------------------------------------------------------------------

[INFO] Total time: 4 seconds

[INFO] Finished at: Sat Dec 08 22:58:57 CET 2007

[INFO] Final Memory: 6M/11M

[INFO] ------------------------------------------------------------------------

Wszytko działa i jest poprawne. W sumnie musi tak być, bo cała aplikacja powinna składać się tylko z interfejsów, klas błędów i jakiś uniwersalnych narzędzi.

Implementacja interfejsu – Kalkulator-JavaEE-EJB

Czas zaimplementować nasz interfejs. W tym celu tworzymy nowy projekt za pomocą mavena

Listing 5. Tworzenie Kalkulator-JavaEE-EJB w mavenie

>mvn archetype:create -DarchrtypeGroupId=org.apache.maven.archetypes /

-DgroupId=eu.runelord.kalkulator.ejb -DartifactId=Kalkulator-JavaEE-EJB

[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] ------------------------------------------------------------------------

[INFO] BUILD SUCCESSFUL

[INFO] ------------------------------------------------------------------------

[INFO] Total time: 2 seconds

[INFO] Finished at: Sat Dec 08 23:07:04 CET 2007

[INFO] Final Memory: 4M/8M

[INFO] ------------------------------------------------------------------------

Teraz należy wprowadzić kilka małych poprawek w pom.xml. Najpierw zmieniłem typ pakowania z jar na ejb, a następnie dodałem jeszcze takie coś:

Listing 6. Plugin ejb3, niestety nie sprawił się najlepiej.


<plugin>

 <groupId>org.apache.maven.plugins</groupId>

     <artifactId>maven-ejb3-plugin</artifactId>

        <configuration>

         <generateClient>false</generateClient>

        </configuration>

</plugin>

Przy okazji okazało się, że plugin do ejb3 nie jest dostęny bezpośrednio z repo apacha. Najpierw trzeba go ściągnąć z sandboxa i lokalnie zainstalować.

Listing 7. instalacja pluginu ejb3 z repozytorium svn

>svn checkout http://svn.apache.org/repos/asf/maven/sandbox/trunk/plugins/maven-ejb3-plugin/

...

>cd maven-ejb3-plugin

>mvn clean compile install

...

[INFO] [plugin:updateRegistry]

[INFO] ------------------------------------------------------------------------

[INFO] BUILD SUCCESSFUL

[INFO] ------------------------------------------------------------------------

[INFO] Total time: 53 seconds

[INFO] Finished at: Sat Dec 08 23:42:30 CET 2007

[INFO] Final Memory: 7M/14M

[INFO] ------------------------------------------------------------------------

Teraz można już śmigać z naszym projektem. Dodajmy zatem implementację naszego interfejsu:

Listing 8. Klasa KalkulatorBean, implemnetacja Kalkulator po stronie serwera


<span class="keywordStyle">package</span> <span class="packageNameStyle">eu.runelord.kalkulator.ejb</span>;

<span class="keywordStyle">import</span> <span class="importNameStyle">javax.jws.WebService</span>;

<span class="keywordStyle">import</span> <span class="importNameStyle">javax.ejb.Stateless</span>;

<span class="keywordStyle">import</span> <span class="importNameStyle">eu.runelord.kalkulator.api.BladWywolania</span>;

<span class="keywordStyle">import</span> <span class="importNameStyle">eu.runelord.kalkulator.api.Kalkulator</span>;

<span class="anotationTypeStyle">@WebService</span>

<span class="anotationTypeStyle">@Stateless</span>

<span class="keywordStyle">public</span> <span class="keywordStyle">class</span> <span class="nonPrimitiveTypeStyle">KalkulatorBean</span> <span class="keywordStyle">implements</span> <span class="nonPrimitiveTypeStyle">Kalkulator</span> {

    <span class="keywordStyle">public</span> <span class="nonPrimitiveTypeStyle">Integer</span> <span class="methodStyle">dodaj</span>(<span class="nonPrimitiveTypeStyle">Integer</span> <span class="variableStyle">a</span>, <span class="nonPrimitiveTypeStyle">Integer</span> <span class="variableStyle">b</span>) <span class="keywordStyle">throws</span> <span class="nonPrimitiveTypeStyle">BladWywolania</span> {

        <span class="keywordStyle">return</span> <span class="variableStyle">a</span> + <span class="variableStyle">b</span>;

    }

    <span class="keywordStyle">public</span> <span class="nonPrimitiveTypeStyle">Integer</span> <span class="methodStyle">odejmij</span>(<span class="nonPrimitiveTypeStyle">Integer</span> <span class="variableStyle">a</span>, <span class="nonPrimitiveTypeStyle">Integer</span> <span class="variableStyle">b</span>) <span class="keywordStyle">throws</span> <span class="nonPrimitiveTypeStyle">BladWywolania</span> {

        <span class="keywordStyle">return</span> <span class="variableStyle">a</span> - <span class="variableStyle">b</span>;

    }

    <span class="keywordStyle">public</span> <span class="nonPrimitiveTypeStyle">Integer</span> <span class="methodStyle">podziel</span>(<span class="nonPrimitiveTypeStyle">Integer</span> <span class="variableStyle">a</span>, <span class="nonPrimitiveTypeStyle">Integer</span> <span class="variableStyle">b</span>) <span class="keywordStyle">throws</span> <span class="nonPrimitiveTypeStyle">BladWywolania</span> {

        <span class="keywordStyle">if</span> (<span class="variableStyle">b</span> == <span class="numericalLiteralStyle">0</span>)

            <span class="keywordStyle">throw</span> <span class="keywordStyle">new</span> <span class="constructorStyle">BladWywolania</span>();

        <span class="keywordStyle">return</span> <span class="variableStyle">a</span> / <span class="variableStyle">b</span>;

    }

    <span class="keywordStyle">public</span> <span class="nonPrimitiveTypeStyle">Integer</span> <span class="methodStyle">pomnoz</span>(<span class="nonPrimitiveTypeStyle">Integer</span> <span class="variableStyle">a</span>, <span class="nonPrimitiveTypeStyle">Integer</span> <span class="variableStyle">b</span>) <span class="keywordStyle">throws</span> <span class="nonPrimitiveTypeStyle">BladWywolania</span> {

        <span class="keywordStyle">return</span> <span class="variableStyle">a</span> * <span class="variableStyle">b</span>;

    }

}

I tu zaczyna się cyrk. Jeżeli chcemy, aby maven zbudował nam taką klasę należy zatroszczyć się o odpowiednie jary. Chodzi tu przedewszystkim o javaee.jar. Jakoś nie ma go w standardowej implementacji, a wyszukanie go w sieci zajęło mi kilka chwil.

Ok trzeba by go jeszcze zainstalować. Jednak ja podejdę do tego problemu w bardziej adekwatny sposób.

W katalogu domowym jest katalog .m2, w którym jest plik settings.xml. Zawiera on konfigurację mavena. Należy w nim dodać:

Listing 9. Dodanie repozytorium glassfisha do mavena

<profiles>

 <profile>

  <id>main1</id>

  <repositories>

   <repository>

    <id>glassfish-repository</id>

    <name>Java.net Repository for Glassfish</name>

    <url>http://download.java.net/maven/glassfish</url>

   </repository>

  </repositories>

 </profile>

</profiles>

<activeProfiles>

 <activeProfile>main1</activeProfile>

</activeProfiles>

W ten sposób dodaliśmy do mavena repozytorium glassfisha, z którego będziemy ciągnąć wszytkie niezbędne nam do życia biblioteki. Na razie potrzebujemy tylko jednej więc do zależności w pom.xml dopisujemy:

Listing 10. Zależność od javaee w pom.xml, pamiętaj zalezność zawsze można wymienić

<dependency>

 <groupId>javax.javaee</groupId>

 <artifactId>javaee</artifactId>

 <version>5.0</version>

</dependency>

Budujemy i instalujemy naszego EJBa:

Listing 11. Instalacna Kalkulator-JavaEE-EJB w mavenie

>mvn clien compile ejb3:ejb3 install

[INFO] Scanning for projects...

[INFO] Searching repository for plugin with prefix: 'ejb3'.

[INFO] -------------------------------------------------------------------------

---

[INFO] Building Kalkulator-JavaEE-EJB

[INFO]    task-segment: [clean, compile, ejb3:ejb3, install]

[INFO] -------------------------------------------------------------------------

...

[INFO] Scanning for projects...

[INFO] Searching repository for plugin with prefix: 'ejb3'.

[INFO] -------------------------------------------------------------------------

---

[INFO] Building Kalkulator-JavaEE-EJB

[INFO]    task-segment: [clean, compile, ejb3:ejb3, install]

[INFO] -------------------------------------------------------------------------

ufff… udało się. W sumie należało by zrobić jeszcze jedną rzecz. Dopisać testy, ale wyszedłem tu z założenia, że logika nie jest aż tak skomplikowana, żeby wymagała testowania. Czas zatem do ostatniego kroku związanego z serwerem, czyli aplikacji EE. Niestety wadą takiego rozwiązania jest związanie naszej aplikacji z Glassfishem, ale tylko pozornie, bo ktoś musi dostarczyć implementacji interfejsów, a tą możemy w razie czego wymienić.

Kalkulator-JavaEE-EAR, zbieramy to wszystko do kupy.

Tradycyjnie zaczynamy od stworzenia projekty w mavenie:

Listing 12. Kalkulator-JavaEE-EAR stworzone w mavenie

>mvn archetype:create -DarchrtypeGroupId=org.apache.maven.archetypes

-DgroupId=eu.runelord.kalkulator.api -DartifactId=Kalkulator-JavaEE-EAR

[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] ------------------------------------------------------------------------

[INFO] BUILD SUCCESSFUL

[INFO] ------------------------------------------------------------------------

[INFO] Total time: 2 seconds

[INFO] Finished at: Sun Dec 09 01:15:51 CET 2007

[INFO] Final Memory: 5M/9M

[INFO] ---------------------------------------------------------------------------

Pierwszą zmianą w pliku jest zmiana pakowania z jar na ear. Następnie należało dodać zależność od naszego EJBa:

Listing 13. Łączymy Kalkulator-JavaEE-EAR z Kalkulator-JavaEE-EJB3

<dependencies>

 <dependency>

  <groupId>eu.runelord.kalkulator.ejb</groupId>

  <artifactId>Kalkulator-JavaEE-EJB</artifactId>

  <version>1.0</version>

  <type>ejb3</type>

 </dependency>

</dependencies>

I uruchomić na Glassfishu.

Problem z pluginem ejb3

Niestety prosto w życiu nie jest, całość się wywaliła. Wywaliła się w sposób dość perfiny ponieważ Glassfish nie chciał przeczytać pliku

Kalkulator-JavaEE-EJB.ejb3. Nie wiem dlaczego, ale nie chciał. Zionął na mnie nieładnym błędem:

Listing 14. Galssfish nie potrafi obsłużyć plików .ejb3


[#|2007-12-09T01:58:05.421+0100|SEVERE|sun-appserver9.1|javax.enterprise.system.tools.deployment

  |_ThreadID=19;_ThreadName=Thread-34;_RequestID=1dcd3d08-a85e-43b3-8564-3f8514457663;|

  Could not expand entry META-INFMANIFEST.MF into destination 

  D:glassfishglassfishdomainsdomain1applicationsj2ee-appsKalkulator-JavaEE-EAR-1.0Kalkulator-JavaEE-EJB-1.0.ejb3

java.io.IOException: Error expanding archive 

  D:glassfishglassfishdomainsdomain1applicationsj2ee-appsKalkulator-JavaEE-EAR-1.0Kalkulator-JavaEE-EJB-1.0.ejb3; 

  please see the server log file for more information

 at com.sun.enterprise.deployment.backend.J2EEModuleExploder.explodeJar(J2EEModuleExploder.java:359)

 at com.sun.enterprise.deployment.backend.J2EEModuleExploder.explodeEar(J2EEModuleExploder.java:296)

 at com.sun.enterprise.deployment.backend.AppDeployer.explodeArchive(AppDeployer.java:285)

 at com.sun.enterprise.deployment.backend.AppDeployer.deploy(AppDeployer.java:207)

 at com.sun.enterprise.deployment.backend.AppDeployer.doRequestFinish(AppDeployer.java:148)

 at com.sun.enterprise.deployment.phasing.J2EECPhase.runPhase(J2EECPhase.java:191)

 at com.sun.enterprise.deployment.phasing.DeploymentPhase.executePhase(DeploymentPhase.java:108)

 at com.sun.enterprise.deployment.phasing.PEDeploymentService.executePhases(PEDeploymentService.java:919)

 at com.sun.enterprise.deployment.phasing.PEDeploymentService.deploy(PEDeploymentService.java:279)

 at com.sun.enterprise.deployment.phasing.PEDeploymentService.deploy(PEDeploymentService.java:788)

 at com.sun.enterprise.management.deploy.DeployThread.deploy(DeployThread.java:187)

 at com.sun.enterprise.management.deploy.DeployThread.run(DeployThread.java:223)

Caused by: java.io.IOException

 at com.sun.enterprise.util.io.FileUtils.openFileOutputStream(FileUtils.java:728)

 at com.sun.enterprise.deployment.backend.J2EEModuleExploder.explodeJar(J2EEModuleExploder.java:331)

 ... 11 more

Caused by: java.io.FileNotFoundException: 

  D:glassfishglassfishdomainsdomain1applicationsj2ee-appsKalkulator-JavaEE-EAR-1.0

  Kalkulator-JavaEE-EJB-1.0.ejb3META-INFMANIFEST.MF (The system cannot find the path specified)

 at java.io.FileOutputStream.open(Native Method)

 at java.io.FileOutputStream.(FileOutputStream.java:179)

 at java.io.FileOutputStream.(FileOutputStream.java:131)

 at com.sun.enterprise.util.io.FileUtils$FileOutputStreamWork.run(FileUtils.java:1591)

 at com.sun.enterprise.util.io.FileUtils.doWithRetry(FileUtils.java:766)

 at com.sun.enterprise.util.io.FileUtils.openFileOutputStream(FileUtils.java:721)

 ... 12 more

|#]

jest dość proste. Bierzemy plik .ear, rozpakowujemy zmieniamy nazwę Kalkulator-JavaEE-EJB.ejb3 na Kalkulator-JavaEE-EJB.jar i zmieniamy w pliku application.xml ejb3 na jar. Pakujemy wrzucamy na serwer. Swoją drogą jest to co najmniej dziwne ponieważ google mowi, że pliki z rozszerzeniem .ejb3 powinny działać. Zresztą olać to. Bierim się za

klienta

Kalkulator-JavaEE-Client

Po stworzeniu projektu mavenem dodajemy mu zależność od Kalkulator-JavaEE-API:

Listing 15. Zalezność od Kalkulator-JavaEE-API w pom.xml dla Kalkulator-JavaEE-Client

<dependencies>

 <dependency>

  <groupId>eu.runelord.kalkulator.api</groupId>

  <artifactId>Kalkulator-JavaEE-API</artifactId>

  <version>1.0</version>

 </dependency>

</dependencies>

Skoro nasz klient ma działać jako klient usłygi sieciowej to potrzebujemy też paczki z namiastkami (ang. stub).

Paczkę taką można wygenerować za pomocą narzędzia wsimport. Trzeba tylko uważać ponieważ domyślnie wygenerowane klasy

znajdją się w tym samym pakiecie co nasze API. Ja to robię w ten sposób:

Listing 16. Generowanie namiastek.

>wsimport -d ws-stub -p eu.runelord.kalkulator.ejb.stub http://localhost:8080/KalkulatorBeanService/KalkulatorBean?wsdl

parsing WSDL...

generating code...

compiling code...

Następnie należy jeszcze tylko całość dodać do jara, przypiąć do classpatha w eclipsie lub zainstalować w mavenie i gotowe.

Teraz należy dodać trochę zależności do pom.xmla w naszym projekcie:

Listing 17. Zależność od javaee i namiastek.

<dependency>

 <groupId>eu.runelord.kalkulator.api</groupId>

 <artifactId>Kalkulator-JavaEE-API</artifactId>

 <version>1.0</version>

</dependency>

<dependency>

 <groupId>eu.runelord.kalkulator.ejb.stub</groupId>

 <artifactId>Kalkulator-JavaEE-WSStub</artifactId>

 <version>1.0</version>

</dependency>

<dependency>

 <groupId>javax.javaee</groupId>

 <artifactId>javaee</artifactId>

 <version>5.0</version>

</dependency>

I już możemy przystąpić do kodowania naszego klienta. Na początek stwórzmy sobie prostą fabrykę kalkulatorów. Interfejs i

kasa implementująca przedstawiają się w następujacy sposób:

Listing 18. Interfejs FabrykaKalkulatorow


<span class="keywordStyle">package</span> <span class="packageNameStyle">eu.runelord.kalkulator.client</span>;

<span class="keywordStyle">import</span> <span class="importNameStyle">eu.runelord.kalkulator.api.Kalkulator</span>;

<span class="keywordStyle">public</span> <span class="keywordStyle">interface</span> <span class="nonPrimitiveTypeStyle">FabrykaKalkulatorow</span> {

    <span class="keywordStyle">public</span> <span class="nonPrimitiveTypeStyle">Kalkulator</span> <span class="methodStyle">stworzKalkulator</span>();

}

Listing 19. Klasa FabrykaKalkulatorowImpl


<span class="keywordStyle">package</span> <span class="packageNameStyle">eu.runelord.kalkulator.client</span>;

<span class="keywordStyle">import</span> <span class="importNameStyle">eu.runelord.kalkulator.api.BladWywolania</span>;

<span class="keywordStyle">import</span> <span class="importNameStyle">eu.runelord.kalkulator.api.Kalkulator</span>;

<span class="keywordStyle">import</span> <span class="importNameStyle">eu.runelord.kalkulator.ejb.stub.BladWywolania_Exception</span>;

<span class="keywordStyle">import</span> <span class="importNameStyle">eu.runelord.kalkulator.ejb.stub.KalkulatorBean</span>;

<span class="keywordStyle">import</span> <span class="importNameStyle">eu.runelord.kalkulator.ejb.stub.KalkulatorBeanService</span>;

<span class="keywordStyle">public</span> <span class="keywordStyle">class</span> <span class="nonPrimitiveTypeStyle">FabrykaKalkulatorowImpl</span> <span class="keywordStyle">implements</span> <span class="nonPrimitiveTypeStyle">FabrykaKalkulatorow</span> {

    <span class="keywordStyle">public</span> <span class="nonPrimitiveTypeStyle">Kalkulator</span> <span class="methodStyle">stworzKalkulator</span>() {

        <span class="keywordStyle">final</span> <span class="nonPrimitiveTypeStyle">KalkulatorBean</span> <span class="variableStyle">kalkulatorBean</span> = (<span class="keywordStyle">new</span> <span class="constructorStyle">KalkulatorBeanService</span>())

                .<span class="methodStyle">getKalkulatorBeanPort</span>();

        <span class="keywordStyle">return</span> <span class="keywordStyle">new</span> <span class="constructorStyle">Kalkulator</span>() {

            <span class="keywordStyle">public</span> <span class="nonPrimitiveTypeStyle">Integer</span> <span class="methodStyle">dodaj</span>(<span class="nonPrimitiveTypeStyle">Integer</span> <span class="variableStyle">arg0</span>, <span class="nonPrimitiveTypeStyle">Integer</span> <span class="variableStyle">arg1</span>)

                    <span class="keywordStyle">throws</span> <span class="nonPrimitiveTypeStyle">BladWywolania</span> {

                <span class="keywordStyle">try</span> {

                    <span class="keywordStyle">return</span> <span class="methodStyle">kalkulatorBean.dodaj</span>(<span class="variableStyle">arg0</span>, <span class="variableStyle">arg1</span>);

                } <span class="keywordStyle">catch</span> (<span class="nonPrimitiveTypeStyle">BladWywolania_Exception</span> <span class="variableStyle">e</span>) {

                    <span class="keywordStyle">throw</span> <span class="keywordStyle">new</span> <span class="constructorStyle">BladWywolania</span>();

                }

            }

            <span class="keywordStyle">public</span> <span class="nonPrimitiveTypeStyle">Integer</span> <span class="methodStyle">odejmij</span>(<span class="nonPrimitiveTypeStyle">Integer</span> <span class="variableStyle">arg0</span>, <span class="nonPrimitiveTypeStyle">Integer</span> <span class="variableStyle">arg1</span>)

                    <span class="keywordStyle">throws</span> <span class="nonPrimitiveTypeStyle">BladWywolania</span> {

                <span class="keywordStyle">try</span> {

                    <span class="keywordStyle">return</span> <span class="methodStyle">kalkulatorBean.odejmij</span>(<span class="variableStyle">arg0</span>, <span class="variableStyle">arg1</span>);

                } <span class="keywordStyle">catch</span> (<span class="nonPrimitiveTypeStyle">BladWywolania_Exception</span> <span class="variableStyle">e</span>) {

                    <span class="keywordStyle">throw</span> <span class="keywordStyle">new</span> <span class="constructorStyle">BladWywolania</span>();

                }

            }

            <span class="keywordStyle">public</span> <span class="nonPrimitiveTypeStyle">Integer</span> <span class="methodStyle">podziel</span>(<span class="nonPrimitiveTypeStyle">Integer</span> <span class="variableStyle">arg0</span>, <span class="nonPrimitiveTypeStyle">Integer</span> <span class="variableStyle">arg1</span>)

                    <span class="keywordStyle">throws</span> <span class="nonPrimitiveTypeStyle">BladWywolania</span> {

                <span class="keywordStyle">try</span> {

                    <span class="keywordStyle">return</span> <span class="methodStyle">kalkulatorBean.podziel</span>(<span class="variableStyle">arg0</span>, <span class="variableStyle">arg1</span>);

                } <span class="keywordStyle">catch</span> (<span class="nonPrimitiveTypeStyle">BladWywolania_Exception</span> <span class="variableStyle">e</span>) {

                    <span class="keywordStyle">throw</span> <span class="keywordStyle">new</span> <span class="constructorStyle">BladWywolania</span>();

                }

            }

            <span class="keywordStyle">public</span> <span class="nonPrimitiveTypeStyle">Integer</span> <span class="methodStyle">pomnoz</span>(<span class="nonPrimitiveTypeStyle">Integer</span> <span class="variableStyle">arg0</span>, <span class="nonPrimitiveTypeStyle">Integer</span> <span class="variableStyle">arg1</span>)

                    <span class="keywordStyle">throws</span> <span class="nonPrimitiveTypeStyle">BladWywolania</span> {

                <span class="keywordStyle">try</span> {

                    <span class="keywordStyle">return</span> <span class="methodStyle">kalkulatorBean.pomnoz</span>(<span class="variableStyle">arg0</span>, <span class="variableStyle">arg1</span>);

                } <span class="keywordStyle">catch</span> (<span class="nonPrimitiveTypeStyle">BladWywolania_Exception</span> <span class="variableStyle">e</span>) {

                    <span class="keywordStyle">throw</span> <span class="keywordStyle">new</span> <span class="constructorStyle">BladWywolania</span>();

                }

            }

        };

    }

}

Wystarczy teraz tylko machnąć króciutki kod, którego zadaniem będzie uruchomienie naszego klienta:

Listing 20. Klasa Main, uruchamiamy klienta


<span class="keywordStyle">package</span> <span class="packageNameStyle">eu.runelord.kalkulator.client</span>;

<span class="keywordStyle">import</span> <span class="importNameStyle">eu.runelord.kalkulator.api.BladWywolania</span>;

<span class="keywordStyle">import</span> <span class="importNameStyle">eu.runelord.kalkulator.api.Kalkulator</span>;

<span class="keywordStyle">public</span> <span class="keywordStyle">class</span> <span class="nonPrimitiveTypeStyle">Main</span> {

    <span class="keywordStyle">public</span> <span class="keywordStyle">static</span> <span class="primitiveTypeStyle">void</span> <span class="methodStyle">main</span>(<span class="nonPrimitiveTypeStyle">String</span>[] <span class="variableStyle">args</span>) <span class="keywordStyle">throws</span> <span class="nonPrimitiveTypeStyle">BladWywolania</span> {

        <span class="nonPrimitiveTypeStyle">FabrykaKalkulatorow</span> <span class="variableStyle">fabrykaKalkulatorow</span> = <span class="keywordStyle">new</span> <span class="constructorStyle">FabrykaKalkulatorowImpl</span>();

        <span class="nonPrimitiveTypeStyle">Kalkulator</span> <span class="variableStyle">kalkulator</span> = <span class="methodStyle">fabrykaKalkulatorow.stworzKalkulator</span>();

        <span class="nonPrimitiveTypeStyle">Integer</span> <span class="variableStyle">w</span> = <span class="methodStyle">kalkulator.dodaj</span>(<span class="numericalLiteralStyle">1</span>, <span class="numericalLiteralStyle">1</span>);

        <span class="methodStyle">System.out.println</span>(<span class="variableStyle">w</span>);

    }

}

Uruchamiamy i… dupa 😛 tego dokładnie oczekiwałem. Cała dotyczczasowa praca była wzorowana na różnych artykułach sieciowych, ale nigdzie nie znalazłem żadnego artykułu poświęconego tworzeniu klienta web service bez kontenera w technologii EJB3. Nawet Sunowskie tutoriale raczej opisują tworzenie Swingowego klienta EJB3 korzystajacego z JNDI niż z WS. OK. Co trzeba zrobić by móc uruchmonić naszego klienta? W sumie niewiele, bo wystarczy dostarczyć mu odpowiednich jarów. Lista jest dość długa, ale niestety takie życie. Nie będę tu przytaczał jej całej, podam tylko wpis w pom.xml, który podepnie nam potrzebne pliki.

Listing 21. Zalezności pozwalając na uruchomienie klienta

<dependency>

 <groupId>com.sun.xml.ws</groupId>

 <artifactId>webservices-rt</artifactId>

 <version>1.1</version>

</dependency>

<dependency>

 <groupId>javax.xml.ws</groupId>

 <artifactId>jaxws-api</artifactId>

 <version>2.1</version>

</dependency>

<dependency>

 <groupId>javax.xml.soap</groupId>

 <artifactId>saaj-api</artifactId>

 <version>1.3</version>

</dependency>

<dependency>

 <groupId>javax.activation</groupId>

 <artifactId>activation</artifactId>

 <version>1.1</version>

</dependency>

Brak kontenera oznacza, niestety, znaczne roztycie aplikacji. Prosty klient zaczął zajmować około 20MB. Z drugiej strony większość plików potrzebnych do uruchomienia można wgrać do oddzielnego katalogu i podlinkować go w CLASSPATH, tak by inne aplikacjie mogły z niego korzystać.

Podsumowanie

Jak widać przygotowanie samego klienta webservice działającego jako aplikacja Stand Alone jest stosunkowo prosta. Najwięcej problemów sprawia wyszukanie i instalacja odpowiednich plików .jar, które stworzą środowisko uruchomieniowe. Dzięki mavenowi jest to wmiarę bez bolesny proces. Z drugiej strony trzeba pamiętać, że ze względu na różnorodność dostępnych serwerów w niektórych przypadkach mogą pojawiać sie błędy. To samo dotyczy się pakietu z namiastkami. Każda zmiana adresu serwera wymaga wygenerowania nowych namiastek i rozesłania ich do klientów. Na całe szczęscie można użyć OSGi, by zautomatyzować ten proces. Jakie są zatem zyski? Niewątpliwie spadek ruchu w sieci. Jeżeli klient korzysta z kilku różnych serwisów to odpada ruch serwer-serwer. Nieątpliwie wzrasta też bezpieczeństow. Poszczególne usługi są od siebie całkowicie odseparowane, a ich łączenie odbywa się dopiero na poziomie klienta. W ten sposób dostęp do danych wymaga posiadania klienta, a dodatkowo można

zabezpieczyć się w taki sposób by tylko określone IP mogły korzystać z naszej usługi. Oznacza to, że potencjany przestępca będzie musiał korzystać z konkretnej maszyny by wydobyć dane. Poziom trudności rośnie.