J2EE Project PRZEMYSŁAW SOŁTAN e-mail: [email protected] (1.1 RC2– build 03.01.2005) Dokumentacja Proces tworzenia niniejszego dokumentu zrealizowano przy użyciu darmowego oprogramowania OpenOffice – http://www.openoffice.org . Historia projektu ✔ ✔ ✔ ✔ ✔ ✔ ✔ ✔ ✔ ✔ ✔ ✔ ✔ 03.01.2005 – opublikowanie wersji 1.1 RC2 (relase candidate)– http://j2ee.ie.tu.koszalin.pl; 03.01.2005 – dodanie przykładu na kod klienta komunikatów JMS; 26.12.2004 – dodanie wstepnej wersji opisu EJB-QL; 16.12.2004 – opracowanie listy tematów projektu; 04.11.2004 – dodanie przykładu obsługi serwera LDAP oraz opis wykorzystania biblioteki log4j; 27.11.2004 – dodanie opisu przykładu komponentu ZoneFacadeBean zawierającego kod obsługi referencji do sesyjnego komponentu ZoneBean; 20.11.2004 – dodanie opisu wykorzystania obiektu Properties oraz parametryzacji komponentów EJB; 16.11.2005 – dodanie przykładu komponentu entity bean CMP 2.0 oraz opisanie wzorca obiektu transferu danych DTO (wersja wstępna); 06.11.2004 – aktualizacja kodu wzorca wyszukiwania usług – ServiceLocator o obsługę wyszukiwania lokalnych komponentów EJB (metoda getLocalHome), zmiana nazwy JNDI komponentu fasady z ejb/FacadeBean na ejb/Facade, dodanie opisu wzorca Singleton; 22.10.2004 - dołączenie dodatku na temat ciekawej biblioteki MockEJB http://www.mockejb.org (atrapy kontenera EJB) za pomocą której można testować kod komponentów EJB bez użycia serwera J2EE; 05.10.2004 – umieszczenie przykładu realizacji wzorców delegata biznesowego i wyszukiwania usług. 04.10.2004 – opublikowanie wersji 1.0; 29.09.2004 – zainicjowanie projektu i ukończenie wersji 1.0 alpha niniejszej dokumentacji do wzorcowego projektu realizowanego w technologii J2EE. Plany rozwojowe • • • • • opis projektu przy użyciu serwera Apache Geronimo i platformy Eclipse rozszerzenie projektu o nowe elementy platformy 1.4 (głównie WebServices) oraz stworzenie wzorcowej aplikacji cienkiego klienta w JSF; rozbudowa projektu o realizację klientów SOA (np. na platformie .NET); rozszerzenie przykładów z wykorzystaniem wzorców projektowych J2EE; implementacja projektu dla serwera JBOSS 4.0 – rozszerzenie opisu o XDoclet; http://j2ee.ie.tu.koszalin.pl -2- Spis treści I. Wstęp........................................................................................................................................... 7 1.1. Literatura.............................................................................................................................7 1.2. Oprogramowanie...............................................................................................................7 II. Serwer aplikacji J2EE.............................................................................................................. 8 2.1. Instalacja..............................................................................................................................8 2.2. Uruchomienie serwera......................................................................................................9 III. Sesyjny komponent EJB......................................................................................................12 3.1. Zaprojektowanie klasy SessionBean............................................................................. 12 3.1.1. Klasa komponentu sesyjnego EJB – FacadeBean.java..............................................................13 3.2. Zaprojektowanie interfejsu domowego........................................................................14 3.2.1. Interfejs domowy - FacadeHome.java...................................................................................... 14 3.3. Zaprojektowanie interfejsu zdalnego........................................................................... 14 3.3.1. Interfejs zdalny - Facade.java...................................................................................................14 3.4. Klasowy diagram UML...................................................................................................15 3.5. Tworzenie nowej aplikacji EAR.....................................................................................16 3.5.1. Deskryptor application.xml..................................................................................................... 17 3.5.2. Deskryptor sun-j2ee-ri.xml......................................................................................................17 3.6. Instalowanie komponentu EJB.......................................................................................17 3.7. Deskryptory......................................................................................................................21 3.7.1. Deskryptor application.xml..................................................................................................... 21 3.7.2. Deskryptor sun-j2ee-ri.xml......................................................................................................21 3.7.3. Deskryptor ejb-jar.xml............................................................................................................. 22 3.8. Deploy aplikacji EAR...................................................................................................... 22 IV. Komunikacja klienta z komponentem EJB..................................................................... 25 4.1. Przykład prostego klienta...............................................................................................25 4.1.1. Klasa klienta - SimpleClient.java.............................................................................................25 4.2. Wzorzec Singleton........................................................................................................... 28 4.2.1. Klasa wzorca singleton - Singleton.java................................................................................. 28 4.2.2. Klasa testowa - TestSingleton.java.......................................................................................... 28 4.3. Wzorzec Lokalizatora Usług.......................................................................................... 29 4.3.1. Klasa lokalizatora usługi – ServiceLocator.java.......................................................................30 4.3.2. Klasa wyjątków lokalizatora usługi – ServiceLocatorException.java...................................... 30 4.3.3. Zmodyfikowana klasa klienta - ServiceLocatorClient.java...................................................... 31 4.4. Klasa właściwości - Properties.......................................................................................32 4.4.1. Fragment kodu z klasy SimpleClient.java............................................................................... 32 4.4.2. Plik właściwości jndi.properies................................................................................................ 32 4.4.3. Zmodyfikowany fragment kodu z klasy SimpleClient.java..................................................... 32 4.5. Wzorzec Delegata Biznesowego....................................................................................33 4.5.1. Klasa delegata biznesowego – BusinessDelegate.java..............................................................34 4.5.2. Klasa klienta – BusinessDelegateClient.java........................................................................... 34 V. Referencje EJB ....................................................................................................................... 36 5.0.1. Klasa komponentu sesyjnego - ZoneBean.java........................................................................ 36 5.0.2. Interfejs domowy - ZoneHome.java......................................................................................... 37 5.0.3. Interfejs zdalny - Zone.java..................................................................................................... 37 5.0.4. Interfejs lokalny-domowy ZoneLocalHome.java......................................................................37 http://j2ee.ie.tu.koszalin.pl -3- 5.0.5. Interfejs lokalny - ZoneLocal.java............................................................................................ 37 5.0.6. Klasa komponentu SessionBean - ZoneFacadeBean.java.........................................................38 5.0.7. Interfejs domowy - ZoneFacadeHome.java.............................................................................. 39 5.0.8. Interfejs zdalny - ZoneFacade.java.......................................................................................... 39 5.0.9. Klasa klienta – ZoneClient.java............................................................................................... 43 5.1. Wzorzec adaptera............................................................................................................ 44 5.1.1. Klasa adaptera SessionBeanAdapter.java................................................................................ 44 5.2. Parametryzacja komponentów EJB............................................................................... 45 5.2.1. Fragment kodu z klasy FacadeBean.java..................................................................................45 5.2.2. Zmodyfikowany fragment kodu z klasy FacadeBean.java........................................................45 5.3. Aplikacja webowa WAR.................................................................................................46 VI. Komponenty Entity EJB...................................................................................................... 47 6.1. Utrwalanie danych.......................................................................................................... 47 6.2. Komponenty CMP 2.X ................................................................................................... 47 6.2.1. Klasa komponetnu Entity EJB (CMP) – UserBean.java......................................................... 47 6.2.2. Interfejs zdalny - User.java......................................................................................................47 6.2.3. Interfejs domowy - UserHome.java..........................................................................................48 6.2.4. Interfejs lokalny - UserLocal.java............................................................................................ 48 6.2.5. Interfejs lokalny-domowy - UserLocalHome.java.................................................................... 48 6.3. GUID .................................................................................................................................49 6.3.1. Klasa Guid.java........................................................................................................................ 49 6.4. Wzorzec Obiektu Transferu Danych............................................................................ 51 6.4.1. Klasa do transferu danych użytkownika - UserData.java....................................................... 51 6.5. 6.6. 6.7. 6.8. 6.9. Wzorzec Facade............................................................................................................... 53 Wzorzec Lokalizatora Usług.......................................................................................... 54 Relacje CMR......................................................................................................................55 Metody Finder i Select.................................................................................................... 56 EJB-QL............................................................................................................................... 56 6.9.1. Składnia zapytania EJB-QL..................................................................................................... 56 6.9.2. Przykładowe metody findXXX................................................................................................ 56 6.9.3. Przykładowe metody selectXxx i selectXxxInEntity............................................................... 56 VII. Transakcje............................................................................................................................ 58 7.1. Model ACID w komponentach EJB.............................................................................. 58 7.2. Wycofanie transakcji....................................................................................................... 58 7.3. Mapowanie metod komponentów EJB.........................................................................58 7.4. Poziomy izolacji transakcji............................................................................................. 59 7.4.1. Fragment desktyptora - ejb-jar.xml..........................................................................................59 VIII. JNDI..................................................................................................................................... 60 8.1. Interfejs Context............................................................................................................... 60 8.1.1. Fragment pliku – SimpleClient.java........................................................................................60 8.2. Interfejs DirContext......................................................................................................... 61 IX. Ldap......................................................................................................................................... 62 9.1. Konfiguracja serwera OpenLdap.................................................................................. 62 9.1.1. Plik konfiguracyjny serwera OpenLDAP - slap.conf.............................................................. 62 9.2. Aplikacja klienta - LdapBrowser................................................................................... 63 9.3. Komunikacja z serwerem............................................................................................... 64 9.3.1. Przykład prostego klienta – LdapClient.java........................................................................... 64 http://j2ee.ie.tu.koszalin.pl -4- X. Komunikaty JMS....................................................................................................................65 10.1. Konfiguracja JMS na serwerze J2EE RI.......................................................................65 10.2. Komponent MDB...........................................................................................................66 10.2.1. Klasa wzorca adaptera dla MDB – MessageDrivenBean.java...............................................66 10.2.2. Klasa komponentu MDB – LogBean.java.............................................................................. 66 10.3. Prosty klient wysyłający komunikaty JMS.................................................................68 10.4. ServiceLocator – rozszerzenie wyszukiwania o kolejki JMS................................... 69 XI. Web Services..........................................................................................................................70 11.1. AXIS................................................................................................................................. 70 XII. Autentykacja i Autoryzacja............................................................................................... 71 12.1. Role użytkowników...................................................................................................... 71 12.2. Komunikacja SSL........................................................................................................... 72 XIII. Testy jednostkowe.............................................................................................................73 13.1. JUnit................................................................................................................................. 73 13.2. Projekt Cactus – osadzanie testów jednostkowych na serwerze J2EE................... 75 XIV. Komponent?!.......................................................................................................................76 14.1. Projekt CUBA................................................................................................................. 76 XV. Log4j.......................................................................................................................................77 15.1. Poziomy rejestracji logów.............................................................................................77 15.2. Przekierowanie logów.................................................................................................. 77 15.3. Definiowanie wzorca ................................................................................................... 77 15.3.1. Kod klasy Log4jClient.java.................................................................................................... 78 15.3.2. Plik właściwości biblioteki log4j – log4j.properties................................................................78 XVI. ANT i MAVEN...................................................................................................................79 16.1. Ant................................................................................................................................... 79 16.1.1. Skrypt ANT'a z J2EE Project – build.xml............................................................................ 79 16.2. Maven.............................................................................................................................. 81 XVII. JBoss i XDoclet..................................................................................................................82 17.1. JBoss.................................................................................................................................82 17.2. XDoclet............................................................................................................................ 82 XVIII. Geronimo......................................................................................................................... 83 XIX. Atrapa EJB ? - MockEJB................................................................................................... 84 19.1. Aplikcja klienta.............................................................................................................. 84 19.1.1. Zmodyfikawana klasa klienta - SimpleMockClient.java........................................................84 XX. Systemy kontroli wersji - Cvs i Svn................................................................................. 86 20.1. CVS.................................................................................................................................. 86 20.2. SVN..................................................................................................................................86 20.2.1. Tworzenie repozytorium SVN...............................................................................................87 XXI. UML...................................................................................................................................... 88 21.1. Diagramy przypadków użycia.................................................................................... 88 XXII. Projekt.................................................................................................................................89 22.1. Założenia nazewnicze................................................................................................... 89 22.2. Diagram klas projektu...................................................................................................90 http://j2ee.ie.tu.koszalin.pl -5- 22.3. Lista tematów................................................................................................................. 91 22.4. Zagadnienia podstawowe............................................................................................ 97 XXIII. Zasoby WWW.................................................................................................................. 98 XXIV. Schematy deskryptorów................................................................................................ 99 XXV. J2EE RI 1.3.1– poprawki i aktualizacje....................................................................... 104 25.1. Ścieżka do bazy Cloudscape...................................................................................... 104 25.1.1. Plik setenv.bat...................................................................................................................... 104 25.2. Bibiblioteka klienta CloudView.................................................................................105 25.2.1. Plik cloudview.bat................................................................................................................ 105 25.3. Obsługa serwera w Win98..........................................................................................105 25.4. Gramatyka sun-j2ee-ri_1_3.dtd..................................................................................105 http://j2ee.ie.tu.koszalin.pl -6- I.Wstęp Niniejszy dokument stanowi opis szkieletu wzorcowego projektu J2EE wykorzystywanego podczas zajęć z przedmiotu “Systemy Rozproszone” realizowanego od 2002 roku w Politechnice Koszalińskiej na Wydziale Elektroniki i Informatyki. W dalszym opisie zastosowano przyrostowy napływ wiedzy przyswajanej wraz z rozbudową opisywanego projektu. Uwaga! Aktualnie jako bazę projektową wykorzystano serwer J2EE RI 1.3.1 ze względu na mniejsze wymagania sprzętowe komputerów w porównaniu z J2EE 1.4 bazującym na serwerze SunOne. Z tego względu część platformy 1.4 nie jest poruszana w tym dokumencie. Dokument nie zawiera opisu poszczególnych typów komponentów EJB uzupełnienie moich wykładów dostępnych pod adresami: i stanowi http://kik.ie.tu.koszalin.pl/portal (jsp, servlet) oraz http://kik.ie.tu.koszalin.pl/systemy (ejb, jndi, ldap, jms, web services) Przemysław Sołtan 1.1. Literatura Na polskim rynku dostępne są trzy wartościowe pozycje dotyczące poruszanych zagadnień (wszystkie wydane przez http://helion.pl): • • • Ed Roman, Scott W. Ambler, Tyler Jewell, “Enterprise JavaBean”. Deepak Alur, John Crupi, Dan Malks, “J2EE Wzorce projektowe – wydanie drugie”; William Crawford, Jonathan Kaplan, “J2EE Stosowanie wzorców projektowych”; Adresy internetowe z wartościowymi materiałami dotyczącymi J2EE: • http://java.sun.com • http://theservletside.com • http://www.javapasion.com 1.2. Oprogramowanie Wszelkie wymagane oprogramowanie jest dostępne na stronie domowej przedmiotu “Systemy Rozproszone” • • http://www.eclipse.org http://jakarta.apache.org http://j2ee.ie.tu.koszalin.pl -7- II.Serwer aplikacji J2EE 2.1. Instalacja Serwer zainstalowano w katalogu d:\java\j2ee (można wybrać także lokalizację domyślną). Po instalacji należy dołączyć zmienną środowiskową J2EE_HOME wskazującą jego lokalizację. Rys. 1. Ustawienie zmiennej środowiskowej J2EE_HOME (Windows 2000) Dodatkowo w zmiennej PATH dopisano lokalizację katalogu %J2EE_HOME%\bin serwera w celu łatwiejszego uruchamiania znajdujących się tam skryptów. Rys. 2. Dodanie ścieżki przeszukiwania katalogu %J2EE_HOME%\bin http://j2ee.ie.tu.koszalin.pl -8- Uwaga! Do prawidłowej pracy serwera aplikacji J2EE RI 1.3.1 wymagana jest poprawka dostępna na stronie autora. Umieszczono w niej poprawną biblioteką kliencką do obsługi bazy Cloudscape (IBM) oraz wsparcie w uruchamianiu serwera na systemie Windows98. Uwaga! Występują problemy przy uruchamianiu serwera przy pomocy javy 1.5. Podczas projektowania wykorzystywano java 1.4.1_2 sdk. W przypadku instalacji dwóch wersji java programy należy uruchamiać z poziomu katalogu bin javy 1.4. Od tego momentu można z konsoli DOS wywoływać poszczególne skrypty wymagane przez serwer. Info! Serwer można również uruchamiać z poziomu innych systemów operacyjnych np. Linux'a (ze strony www.java.sun.com dostępne są odpowiednie wersje instalacyjne) 2.2. Uruchomienie serwera Pierwszym etapem jest uruchomienie serwera baz danych. W implementacji J2EE RI 1.3.1 zastosowano serwer Cloudscape (w wersji J2EE 1.4 zmieniono serwer aplikacji na SunOne, a serwer baz danych na PointBase). Otwieramy konsolę DOS (w Windows2000 jest to polecenie cmd.exe) i wpisujemy: cloudscape – start Rys. 3. Konsola dos z uruchomionym serwerem bazy danych Cloudscape Po otwarciu kolejnej konsoli wpisujemy: j2ee -verbose i następuje uruchomienie serwera aplikacyjnego J2EE. Info! Podczas uruchamiania serwera wyświetlane zostają informacje o dowiązaniach źródeł baz danych (Binding DataSource) oraz kolejek i fabryk komunikatów (Binding: < JMS). W dalszej części projektu będziemy wykorzystywali źródło bazy danych CloudscapeDB powiązanego nazwą jdbc/Cloudscape w celu utrwalania komponentów Entity Bean. http://j2ee.ie.tu.koszalin.pl -9- Rys. 4. Konsola dos z uruchomionym serwerem aplikacyjnym J2EE Serwer udostepnia testową stronę www pod adresem http://localhost:8000 Rys. 5. Strona startowa serwera J2EE Do zarządzania serwerem wykorzystuje się aplikację grubego klienta uruchomienie programu: deploytool http://j2ee.ie.tu.koszalin.pl -10- Rys. 6. Deploytool – program do zarządzania serwerem J2EE RI 1.3.1 Dla ułatwienia pracy z serwerem można stworzyć odnośniki na pulpicie do najczęściej używanych skryptów wraz z wymaganymi parametrami. Rys. 7. Przykładowy folder ze standardowymi skrótami do obsługi serwera J2EE http://j2ee.ie.tu.koszalin.pl -11- III.Sesyjny komponent EJB Zadanie: • zaprojektować komponent sesyjny SessionBean składający się z trzech plików źródłowych (dwóch interfejsów – domowego FacadeHome i zdalnego Facade oraz klasy FacadeBean) • wywołać przykładową metodę biznesową test() komponentu sesyjnego FacadeBean za pomocą klasy klienta i usługi JNDI. 3.1. Zaprojektowanie klasy SessionBean Jako środowisko projektowe wykorzystano darmową platformę Eclipse. Stworzono przykładowy projekt j2ee_project i dodano bibliotekę j2ee.jar zawierającą niezbedne klasy i interfejsy dla platformy J2EE. Info! Projekt j2ee_project to w rzeczywistości zestaw projektów j2ee_projectX, gdzie X określa numer projektu. Zastosowano numerację przyrostową liczoną od zera w celu porównywania zmian pomiędzy poszczególnymi fazami rozwijanego projektu. Uwaga! Biblioteka j2ee.jar jest dostępna w katalogu %J2EE_HOME%\lib zainstalowanego serwera J2EE RI 1.3.1 Rys. 8. Pusty projekt JAVA z dołączoną biblioteką J2EE Info! Dla platformy Eclipse istnieje kilka projektów wspierających tworzenie aplikacji w J2EE: (darmowe Lomboz i JBoss-IDE, czy też komercyjne projekty Omondo ze wsparciem UML, MyEclipse). W ramach zajęć będziemy jednak korzystać ze standardowych narzędzi Eclipse. http://j2ee.ie.tu.koszalin.pl -12- Klasa FacadeBean budowana jest na podstawie interfejsu SessionBean zawartego w bibliotece javax.ejb.SessionBean. Rys. 9. Hierarchia klas i interfejsów dla klasy FacadeBean ( - klasa, -interfejs) Ze względu na zastosowanie interfejsu w klasie muszą zostać zadeklarowane wszystkie metody z nim związane (metody z ramki): setSessionContext(SessionContext),ejbRemote(),ejbActivate(),ejbPasivate(). Info! Dla lepszego zaobserwowania kolejności wywołana metod w projekcie w niektórych miejscach dodano kod wysyłania komunikaty na System.out: np. System.out.println("[INFO] FacadeUserBean: ejbRemove()"); 3.1.1. Klasa komponentu sesyjnego EJB – FacadeBean.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 package systemy.session; import javax.ejb.SessionBean; import javax.ejb.SessionContext; public class FacadeBean implements SessionBean { public FacadeBean(){} public void ejbCreate() { System.out.println("[INFO] FacadeUserBean: ejbCreate()"); } public void ejbActivate() { System.out.println("[INFO] FacadeUserBean: ejbActivate()"); } public void ejbPassivate() { System.out.println("[INFO] FacadeUserBean: ejbPassivate()"); } public void ejbRemove() { System.out.println("[INFO] FacadeUserBean: ejbRemove()"); } public void setSessionContext(SessionContext ctx) { System.out.println("[INFO] FacadeBean: setEntityContext(SessionContext "+ctx+")"); } //====================================================================== //metoda biznesowa test() //====================================================================== public String test() { System.out.println("[INFO] FacadeBean: test()"); return "test"; } //====================================================================== } Dodatkowo w klasie zdefiniowano jedną testową metodę biznesową test(), której wywołanie powoduje zwrócenie obiektu Stringg zawierajacego tekst “test”. Info! W dalszych etapach projektowania będziemy dodawać do klasy FacadeBean inne metody bisnesowe podobne jak przykładowa metoda test(). http://j2ee.ie.tu.koszalin.pl -13- 3.2. Zaprojektowanie interfejsu domowego Rys. 10. Hierarchia interfejsów dla interfejsu domowego FacadeHome 3.2.1. Interfejs domowy - FacadeHome.java 40 41 42 43 44 45 46 47 48 49 50 package systemy.session; import java.rmi.RemoteException; import javax.ejb.CreateException; import javax.ejb.EJBHome; public interface FacadeHome extends EJBHome { Facade create() throws RemoteException, CreateException; } W interfejsie domowym umieszczono metodę Facade create() która będzie zdalnie wywoływana przez kod klienta. 3.3. Zaprojektowanie interfejsu zdalnego Rys. 11. Hierarchia interfejsów dla interfejsu zdalnego Facade 3.3.1. Interfejs zdalny - Facade.java 51 52 53 54 55 56 57 58 59 60 package systemy.session; import java.rmi.RemoteException; import javax.ejb.EJBObject; public interface Facade extends EJBObject { public String test() throws RemoteException; //test } W interfejsie zdalnym umieszczono metodę biznesową String test() wywoływaną zdalnie przez klienta. Info! W interfejsie zdalnym umieszcza się deklaracje metod biznesowych zawartych w klasie komponentu EJB. Nie jest konieczne umieszczanie deklaracji dla wszystkich metod biznesowych – część z nich może być dostępna prywatnie w obszarze komponentu lub udostępniona przy pomocy interfejsu lokalnego (omówienie interfejsów lokalnych zostanie dokonane w dalszej części projektu) innym klientom uruchamianym przy pomocy jej samej maszyny wirtualnej JAVA. http://j2ee.ie.tu.koszalin.pl -14- 3.4. Klasowy diagram UML Zaprojektowano trzy pliki (interfejs domowy, interfejs zdalny oraz klasę komponentu). Pomiędzy interfejsami FacadeHome i Face istnieje zależność <<use>> ponieważ metoda create() zadeklarowana w interfejsie FacadeHome zwraca obiekt zdalny budowany na podstawie interfejsu zdalnego Facade. Reszta zależności nie jest realizowana na poziomie kodu, ale opisywana przy pomocy dodatkowych plików XML – deskryptorów. Rys. 12. Diagram UML przedstawiający zaprojektowany komponent EJB (Omondo) Rys. 13. Widok drzewa projektu (Eclipse IDE) Uwaga! Komponet został nazwany FacadeBean (w wykładzie WitajBean) ponieważ w dalszej części opisu na jego bazie będzie realizowany wzorzec Fasady (Facade). Proces projektowania klas jest zakończony. Reszta operacji instalacji komponentu polega na jego konfiguracji przy pomocy deskryptorów. W przypadku serwera J2EE RI wykorzystuje się do tego celu program Deploytool. Info! W przypadku innych serwerów (np. JBOSS) proces generacji deskryptorów i plików Java wraz z instalacją całej aplikacji może być przyspieszony przez użycie narzędzi Xdoclet oraz Ant. Niestety dla serwera J2EE RI 1.3.1 brak jest wtyczki umożliwiającej takie rozwiązanie. http://j2ee.ie.tu.koszalin.pl -15- 3.5. Tworzenie nowej aplikacji EAR Proces instalacji polega na stworzeni aplikacji EAR. W tym celu wybieramy z menu File→New→Application i podajemy nazwę archiwum ear np. systemy.ear. Rys. 14. Tworzenie nowej aplikacji - archiwum EAR (Deploytool) Rys. 15. Aplikacja EAR i jej desktyptory (Deploytool) Aplikacja EAR posiada dwa deskryptory: standardowy application.xml or deskryptor określonego serwera – dla J2EE RI jest to sun-j2ee-ri.xml (w dodatkowym deskryptorze można deklarować specyficzne właściwości serwera J2EE udostepnione przez danego producenta). http://j2ee.ie.tu.koszalin.pl -16- 3.5.1. Deskryptor application.xml 61 62 63 64 65 66 67 68 <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE application PUBLIC '-//Sun Microsystems, Inc.//DTD J2EE Application 1.3//EN' 'http://java.sun.com/dtd/application_1_3.dtd'> <application> <display-name>systemy</display-name> <description>Application description</description> </application> 3.5.2. Deskryptor sun-j2ee-ri.xml 69 70 71 72 73 74 75 <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE j2ee-ri-specific-information PUBLIC '-//Sun Microsystems Inc.//DTD J2EE Reference Implementation 1.3//EN' 'http://localhost:8000/sun-j2ee-ri_1_3.dtd'> <j2ee-ri-specific-information> <rolemapping /> </j2ee-ri-specific-information> Oba deskryptory są budowane zgodnie z gramatyką zawartą w ich dokumentach DTD. 3.6. Instalowanie komponentu EJB Proces instalacji komponentu EJB polega na mapowaniu zaprojektowanych klas w wyniku czego powstaje deskryptor rozmieszczenia. W tym celu wykorzystuje się wizarda New Enterprise Bean Wizard wywoływany z menu File→New→Enterprise Bean. Rys. 16. Enterprise Bean Wizard (Deploytool) W procesie mapowania komponentu dokonuje się wyboru plików java, które mają wchodzić w skład naszego komponentu. W tym przypadku będą to: Facade.class, FacadeHome.class oraz FacadeBean.class. http://j2ee.ie.tu.koszalin.pl -17- Uwaga! Należy pamiętać, że dodajemy skompilowany kod naszych klas (bytecode) *.class, a nie ich źródła *.java. Rys. 17. Dodawanie plików klas komponentu EJB (Deploytool) Dodane pliki muszą mieć zachowaną hierarchię pakietów w postaci odpowiednich katalogów (np. systemy.session.Facade.class). Rys. 18. Wizard tworzący mapowanie komponentu EJB (Deploytool) http://j2ee.ie.tu.koszalin.pl -18- W przykładzie zamapowano komponet sesyjny jako stanowy – Stateful. Taki komponent będzie mógł reprezentować kod klienta na serwerze J2EE (każdy klient będzie posiadał własną instancję tego komponentu). Info! W przykładzie jest wywoływana prosta metoda test() dla której wystarczy zastosować komponent sesyjny bezstanowy – Stateless, ale ze względu na rozbudowę projektu w dalszej części zastosowano odpowiednik stanowy Stateful. Dalsze ustawienia Wizarda pozostawiamy jako domyśle. Są to głównie opcje dotyczące transakcji, konfigurowania parametrów uzytkownika, referencji do innych komponentów EJB, czy zasobów określanych przez nazwy JNDI oraz bezpieczeństwa. Do tych elementów powrócimy w dalszym opisie projektu. Rys. 19. Mapowanie klas komponentu FacadeBean (Deploytool) Uwaga! Należy zwrócić uwagę, żę mapowanie klas komponentów dotyczy nie tylko nazwy klas, ale całej hierarchi pakietów. Ma to szczególne znaczenie podczas definiowania referencji do innych komponentów EJB. Problem ten zostanie omówiony podczas opisu wzorca Fasady Sesji. Ostatnią operacją przed umieszczeniem całego kodu aplikacji EAR na serwerze jest nadanie naszemu komponentowi nazwy JNDI. Gdy zapomnimy to zrobić to program deploytool przypomni nam o tym podczas wywoływania opcji deploy. http://j2ee.ie.tu.koszalin.pl -19- Rys. 20. Wiązanie komponentu z nazwą JNDI (Deploytool) Nazwa JNDI może być dowolna, ale warto zastosować dodatkowe konteksty dla różnych typów zasobów w celu łatwiejszej ich identyfikacji (np. ejb/FacadeBean). W ten sposób na serwerze zdefiniowane są również inne zasoby np. źródła baz danych (np. jdbc/Cloudscape), czy też kolejki komunikatów (np. jms/Topic). Dodatkowo poprawność ustawień oraz kod java można sprawdzić przy pomocy weryfikatora (Verifier) poprzez wybór z menu opcji Edit→Verifer... Rys. 21. Weryfikacja komponentu (Deploytool – Verifier) http://j2ee.ie.tu.koszalin.pl -20- 3.7. Deskryptory 3.7.1. Deskryptor application.xml 76 77 78 79 80 81 82 83 84 85 86 <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE application PUBLIC '-//Sun Microsystems, Inc.//DTD J2EE Application 1.3//EN' 'http://java.sun.com/dtd/application_1_3.dtd'> <application> <display-name>systemy</display-name> <description>Application description</description> <module> <ejb>ejb-jar-ic.jar</ejb> </module> </application> Deskryptor application.xml zawiera informację, że w aplikacji występuje dodatkowy moduł ejb-jar-ic.jar 3.7.2. Deskryptor sun-j2ee-ri.xml 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE j2ee-ri-specific-information PUBLIC '-//Sun Microsystems Inc.//DTD J2EE Reference Implementation 1.3//EN' 'http://localhost:8000/sun-j2ee-ri_1_3.dtd'> <j2ee-ri-specific-information> <rolemapping /> <enterprise-beans> <module-name>ejb-jar-ic.jar</module-name> <unique-id>0</unique-id> <ejb> <ejb-name>FacadeBean</ejb-name> <jndi-name>ejb/FacadeBean</jndi-name> <ior-security-config> <transport-config> <integrity>supported</integrity> <confidentiality>supported</confidentiality> <establish-trust-in-target>supported</establish-trust-in-target> <establish-trust-in-client>supported</establish-trust-in-client> </transport-config> <as-context> <auth-method>username_password</auth-method> <realm>default</realm> <required>false</required> </as-context> <sas-context> <caller-propagation>supported</caller-propagation> </sas-context> </ior-security-config> <gen-classes /> </ejb> </enterprise-beans> </j2ee-ri-specific-information> Deskryptor sun-j2ee-ri.xml zawiera dodatkowe informacje. Najważniejszą z nich jest mapowanie komponentu FacadeBean jako nazwa JNDI – ejb/FacadeBean. Reszta informacji na razie nie jest istotna i dotyczy dodatkowych ustawień serwera (bezpieczeństwo). Wewnątrz archiwum ejb-jar-ir.jar znajduje się kolejny deskryptor zawierający więcej informacji o przechowywanym komponencie EJB. http://j2ee.ie.tu.koszalin.pl -21- 3.7.3. Deskryptor ejb-jar.xml 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE ejb-jar PUBLIC '-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 2.0//EN' 'http://java.sun.com/dtd/ejb-jar_2_0.dtd'> <ejb-jar> <display-name>SystemyEjb</display-name> <enterprise-beans> <session> <display-name>FacadeBean</display-name> <ejb-name>FacadeBean</ejb-name> <home>systemy.session.FacadeHome</home> <remote>systemy.session.Facade</remote> <ejb-class>systemy.session.FacadeBean</ejb-class> <session-type>Stateful</session-type> <transaction-type>Bean</transaction-type> <security-identity> <description></description> <use-caller-identity></use-caller-identity> </security-identity> </session> </enterprise-beans> <assembly-descriptor> <method-permission> ... </method-permission> </assembly-descriptor> </ejb-jar> 3.8. Deploy aplikacji EAR Proces rozmieszczenia wywołyje się z menu: Tools→Deploy. Rys. 22. Eksportowanie biblioteki jar dla aplikacji klienckiej Podczas procesu rozmieszczania można wybrać opcję eksportowania kodu biblioteki JAR służącej do zdalnego wywoływania komponentu. http://j2ee.ie.tu.koszalin.pl -22- Podczas długotrwałego procesu rozmieszczania można przeglądać komunikaty pojawiające się podczas tego procesu na konsoli serwera J2EE oraz programu Deploytool. Rys. 23. Konsola serwera J2EE RI Na podstawie przygotowanego kodu java w procesie rozmieszczania następuje generacja klas. Infrastruktura komunikacji zdalnego wywoływania kodu klienta realizowana jest przy pomocy protokołu RMI-IIOP. W celu generacji wymaganego kodu wykorzystuje się program rmic standardowo dostępny z JDK. Info! Więcej informacji na temat RMI można odnaleźć na witrynie przedmiotu “Programowanie dla Sieci” - http://java.ie.tu.koszalin.pl Rys. 24. Konsola programu Deploytool Rys. 25. Zakończenie rozmieszczania aplikacji (Deploytool – Deploy) http://j2ee.ie.tu.koszalin.pl -23- Serwer aplikacji J2EE RI 1.3.1 na porcie 9191 udostępnia stronę WWW z której można pobrać archiwum jar z klasami potrzebnymi aplikacjom klienckim do zdalnego komunikowania się z komponentem EJB (stub'y dla komunikacji RMI-IIOP). Rys. 26. Strona www udostępniająca kliencki kod dostępu do komponentu EJB Rys. 27. Podgląd listy aplikacji EAR uruchomionych na serwerze (Deploytool – Deploy) Po rozmieszczeniu aplikacji można ją odinstalować bez restartu serwera – opcja Undeploy. http://j2ee.ie.tu.koszalin.pl -24- IV.Komunikacja klienta z komponentem EJB Komponent EJB został zainstalowany na serwerze. Serwer ochrania komponent stosując określone zasady bezpieczeństwa (różne poziomy ochrony transakcji oraz autoryzację dostępu do metod komponentu za pomocą ról użytkowników). W przypadku, gdy dla komponentu EJB zdefiniowano interfejsy: domowy i zdalny (np. FacadeHome i Facade) można się z nim skomunikować przy pomocy protokołu RMI-IIOP. W tym celu serwer udostępnia serwer nazewniczy JNDI nasłuchujący na porcie 1050. 4.1. Przykład prostego klienta Zaprezentowany prosty kod klienta realizuje komunikację z komponentem EJB. Następuje wywołanie kodu komunikującego się z serwerem JNDI nasłuchującym na porcie 1050. Serwer odnajduje komponent (referencję do obiektu domowego Home) poprzez podanie nazwy JNDI (np. otrzymuję obiekt FacadeHome po podaniu nazwy “ejb/FacadeBean”). 4.1.1. Klasa klienta - SimpleClient.java 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 package systemy.client; import java.util.Properties; import import import import import javax.naming.Context; javax.naming.InitialContext; javax.rmi.PortableRemoteObject; systemy.session.FacadeHome; systemy.session.Facade; public class SimpleClient { public static void main(String[] args) { Properties env = new Properties(); //J2EE RI 1.3.1 env.put("java.naming.factory.initial","com.sun.enterprise.naming.SerialInitContextFactory"); env.put("java.naming.provider.url", "iiop://localhost:1050"); try { Context ctx = new InitialContext(env); Object objref = ctx.lookup("ejb/FacadeBean"); //nazwa JNDI FacadeHome home = (FacadeHome) PortableRemoteObject.narrow(objref,FacadeHome.class); Facade w = home.create(); System.out.println(w.test()); //test } catch (Exception e) { System.err.println(e); } } } http://j2ee.ie.tu.koszalin.pl -25- Rys. 28. Pakiet aplikacji klienta i aplikacji J2EE 4.1.2. Rys. 29. Diagram UML przedstawiający zaprojektowany komponent EJB (Omondo) z klasą klienta Info! Serwery aplikacyjne posiadają serwer nazw JNDI do którego dostęp realizowany jest przy pomocy tzw. Fabryk. Dodatkowo każdy z serwerów posiada lokalizację i numer port na którym działa usługa nazewnicza JNDI. //JBoss env.put("java.naming.factory.initial","org.jnp.interfaces.NamingContextFactory"); env.put("java.naming.provider.url","localhost:1099"); //SunOne 8 env.put("java.naming.factory.initial","com.sun.jndi.cosnaming.CNCtxFactory"); env.put("java.naming.provider.url", "iiop://localhost:3700"); Do przetestowania kodu klasy klienta stworzono nowy projekt j2ee_projectX_client. Do prawidłowej pracy wymagana jest biblioteka j2ee.jar tak jak w przypadku poprzedniego projektu oraz plik systemyClient.jar wygenerowany przez serwer J2EE w procesie rozmieszczenia aplikacji (Deploy). http://j2ee.ie.tu.koszalin.pl -26- Rys. 30. Klasa klienta wywołana z poziomu Eclipse IDE Po wywołaniu kodu klienta na konsoli pojawia się wynik wywołania metody witaj() komponentu EJB. Rys. 31. Konsola serwera J2EE wyświetla komunikaty System.out zawarte w klasie FacadeBean (wywołanie metod setEntityContext, ejbCreate oraz test) Aplikacja klienta może zostać zrealizowana jako zwykła klasa Java lub też kod może zostać umieszczony w aplikacji web'owej WAR (jako strona JSP, servlet, lub JavaBean). Kod klienta może zostać wywołany zdalnie z innego komputera (innej wirtualnej maszyny Java). http://j2ee.ie.tu.koszalin.pl -27- 4.2. Wzorzec Singleton Podczas tworzenia projektów przy użyciu języka java istnieją sytuacje, w których wymagana jest klasa na podstawie której będziemy można tworzyć tylko pojedynczy obiekt. W tym celu można skorzystać z wzorca projektowego singleton. 4.2.1. Klasa wzorca singleton - Singleton.java 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 public class Singleton { private static Singleton instance; static { instance = new Singleton(); } public static Singleton getInstance() { return instance; } private String test; public String getTest() { return test; } public void setTest(String test) { this.test = test; } } 4.2.2. Klasa testowa - TestSingleton.java 207 208 209 210 211 212 213 214 215 216 217 public class TestSingleton { public static void main(String[] args) { Singleton s1 = Singleton.getInstance(); s1.setTest("test"); } } Singleton s2 = Singleton.getInstance(); System.out.println(s2.getTest()); W przykładzie metody setTest(String test) i getTest() wykonywane są na tej samej instancji klasy Singleton.java mimo, że nie zostały zdefiniowane jako statyczne. Zaprezentowany wzorzec zostanie użyty przy projektowaniu lokalizatora usług (ServiceLocator). http://j2ee.ie.tu.koszalin.pl -28- 4.3. Wzorzec Lokalizatora Usług Budowa kodu prostego klienta SimpleClient.java obarczona była koniecznością znajomości obsługi lokalizowania usług przy pomocy JNDI. Aby dokonać izolacji klienta od problemów z obsługą JNDI stosuje się wzorzec lokalizatora usług (Service Locator). http://j2ee.ie.tu.koszalin.pl -29- 4.3.1. Klasa lokalizatora usługi – ServiceLocator.java 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 package systemy.client; import import import import import import import import java.util.Collections; java.util.HashMap; java.util.Map; java.util.Properties; javax.ejb.EJBHome; javax.naming.InitialContext; javax.naming.NamingException; javax.rmi.PortableRemoteObject; public class ServiceLocator { private InitialContext initialContext; private Map cache; private static ServiceLocator instance; static { try { } instance = new ServiceLocator(); }catch(ServiceLocatorException e){ System.err.println(e); } public static ServiceLocator getInstance() { System.out.println(" ServiceLocator: getInstance()"); return instance; } public ServiceLocator() throws ServiceLocatorException { System.out.println(" ServiceLocator: konstruktor ServiceLocator()"); Properties env = new Properties(); //kod mozna przeniesc do pliku properties //J2EE RI 1.3.1 env.put("java.naming.factory.initial","com.sun.enterprise.naming.SerialInitContextFactory"); env.put("java.naming.provider.url", "iiop://localhost:1050"); try { } } initialContext = new InitialContext(env); cache = Collections.synchronizedMap(new HashMap()); }catch(NamingException e){ throw new ServiceLocatorException(e); }catch(Exception e){ throw new ServiceLocatorException(e); } Występowanie wyjątków NamingException związanych z obsługą JNDI zostało przejęte w klasie ServiceLocator w ten sposób, że ich wystąpienie powoduje wywołanie nowych wyjątków ServiceLocatorException zdefiniowanych przy pomocy własnej klasy. W ten sposób klient nie musi importować obsługi javax.naming.NamingException. Podobna sytuacja dotyczy komunikacji przy pomocy RMI. Klasa ServiceLocator jest dla klienta widziana jako klasa lokalna i klient nie potrzebuje importować javax.rmi.PortableRemoteObject. 4.3.2. Klasa wyjątków lokalizatora usługi – ServiceLocatorException.java 270 271 272 273 274 275 276 277 package systemy.client; public class ServiceLocatorException extends Exception { public ServiceLocatorException(Throwable t) { super(t); } } http://j2ee.ie.tu.koszalin.pl -30- 4.3.3. Zmodyfikowana klasa klienta - ServiceLocatorClient.java 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 package systemy.client; import systemy.session.Facade; import systemy.session.FacadeHome; import systemy.util.ServiceLocator; public class ServiceLocatorClient { public static void main(String[] args) { try { FacadeHome home = (FacadeHome) ServiceLocator.getInstance().getRemoteHome("ejb/Facade", FacadeHome.class) Facade facade = home.create(); //mamy obiekt domowy wiec tworzymy obiekt zdalny String str = facade.test(); System.out.println(str); } } } catch (Exception e) { System.err.println(e); } Z klasy klienta usunięto kod komunikacji z serwerem JNDI, który został przeniesiony do klasy lokalizatora usług ServiceLocator. http://j2ee.ie.tu.koszalin.pl -31- 4.4. Klasa właściwości - Properties Podczas projektowania kodu klienta SimpleClient.java oraz lokalizatora usług ServiceLocator.java zastosowano klasę Properties. Rys. 32. Hierarchia klas i interfejsów oraz lista publicznych metod dla klasy Properties Klasa Properties udostępnia metody ładowania, pobierania, wyświetlania oraz zapisu zawartych danych przechowywanych w obiekcie w postaci par klucz=wartość. 4.4.1. Fragment kodu z klasy SimpleClient.java 303 304 305 306 307 Properties env = new Properties(); //J2EE RI 1.3.1 env.put("java.naming.factory.initial","com.sun.enterprise.naming.SerialInitContextFactory"); env.put("java.naming.provider.url", "iiop://localhost:1050"); Umożliwia ona przechowywanie danych w postaci klucza i jego wartości. Stosowanie w kodzie java danych konfiguracyjnych utrudnia ich modyfikację, ponieważ przy ich zmianie należy ponownie skompilować kod java. Z tego względu zawartośc obiektu env klasy Properties, zamiast wypełniać przy pomocy metody put(klucz,wartość), można pobrać z zewnętrznego pliku. 4.4.2. Plik właściwości jndi.properies 308 309 310 311 312 #============================================================== #J2EE RI 1.3.1 #============================================================== java.naming.factory.initial=com.sun.enterprise.naming.SerialInitContextFactory java.naming.provider.url=iiop://localhost:1050 4.4.3. Zmodyfikowany fragment kodu z klasy SimpleClient.java 313 314 315 316 317 318 319 320 321 Properties env = new Properties(); try { env.load(new FileInputStream("systemy/client/jndi.properties")); } catch (IOException e) { System.err.println(e); } http://j2ee.ie.tu.koszalin.pl -32- 4.5. Wzorzec Delegata Biznesowego Celem stosowania wzorca delegata biznesowego jest izolacja kodu klienta od konieczności obsługi kodu związanego z daną technologią. W rozpatrywanym przypadku izolacji poddana zostanie odsługa komponentów EJB w ten sposób, że klient nie będzie miał pojęcia o technologicznych aspektach zahermetyzowanych operacji. Wzorzec delegata biznesowego przypomina współpracę klienta i maklera giełdowego. Klienta interesuje dokonywanie podstawowych operacji biznesowych, a makler jest pośrednikem, który zna szczegóły dokonywania operacji. Rys. 33. Diagram UML aplikacji klienta wykorzystującej wzorzec Delegata Biznesowego i Lokalizatora Usługi http://j2ee.ie.tu.koszalin.pl -33- 4.5.1. Klasa delegata biznesowego – BusinessDelegate.java 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 package systemy.client; import java.rmi.RemoteException; import javax.ejb.CreateException; import javax.resource.ResourceException; import systemy.session.Facade; import systemy.session.FacadeHome; public class BusinessDelegate { private Facade facade; public BusinessDelegate() throws ResourceException { System.out.println(" BusinessDelegate: konstruktor BusinessDelegate()"); try { FacadeHome home = (FacadeHome) DelegateServiceLocator.getInstance() .getRemoteHome("ejb/FacadeBean", FacadeHome.class); //mamy obiekt domowy wiec tworzymy obiekt zdalny facade = home.create(); } catch (DelegateServiceLocatorException e) { throw new ResourceException("Error: DelegateServiceLocatorException"); }catch(CreateException e){ throw new ResourceException("Error: Ejb CreateException"); }catch(RemoteException e){ throw new ResourceException("Error: Ejb RemoteException"); } } //======================================================================================== // metody biznesowe //======================================================================================== public String getTest() throws ResourceException { System.out.println(" BusinessDelegate: getTest()"); try { return facade.test(); //wywolanie metody biznesowej w fasadzie }catch (RemoteException e){ throw new ResourceException(e.toString()); } } //======================================================================================== } 4.5.2. Klasa klienta – BusinessDelegateClient.java 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 package systemy.client; import javax.resource.ResourceException; public class Client { public static void main(String[] args) { System.out.println("Client: main(String[] args)"); try { BusinessDelegate delegate = new BusinessDelegate(); System.out.println(delegate.getTest()); //pośrednie wywolanie metody test() komponentu FasadyBean } } }catch (ResourceException e) { System.err.println(e); } http://j2ee.ie.tu.koszalin.pl -34- Rys. 34. Konsola klienta z dodatkowymi komunikatami podczas wywołania metody test() komponentu FacadeBean http://j2ee.ie.tu.koszalin.pl -35- V.Referencje EJB 5.0.1. Klasa komponentu sesyjnego - ZoneBean.java 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 package systemy.session.zone; import java.util.Calendar; import java.util.GregorianCalendar; import java.util.TimeZone; import import import import import javax.ejb.EJBException; javax.ejb.SessionBean; javax.ejb.SessionContext; javax.naming.InitialContext; javax.naming.NamingException; public class ZoneBean implements SessionBean { private static final long serialVersionUID = 3L; private SessionContext context = null; //====================================================================== //metoda biznesowa getTimeZoneString //====================================================================== private String getTimeZoneString(String zone) { System.out.println("ZoneBean: getTimeZoneString()"); Calendar cal = new GregorianCalendar(TimeZone.getTimeZone(zone)); return cal.get(Calendar.HOUR_OF_DAY)+":"+cal.get(Calendar.MINUTE); } //====================================================================== //metoda biznesowa getLocalTimeString //====================================================================== public String getLocalTimeZoneString() { System.out.println("ZoneBean: getLocalTimeZoneString()"); Calendar cal = new GregorianCalendar(); return cal.get(Calendar.HOUR_OF_DAY)+":"+cal.get(Calendar.MINUTE); } //====================================================================== //metoda biznesowa getPolandTimeZoneString //====================================================================== public String getPolandTimeZoneString() { } System.out.println("ZoneBean: getPolandZoneTimeString()"); return getTimeZoneString("Europe/Warsaw"); public String getContextTimeZoneString() { System.out.println("ZoneBean: getContextTimeZoneString()"); String timeZone = null; try { InitialContext initialContext = new InitialContext(); timeZone = (String) initialContext.lookup("java:comp/env/timeZone"); System.out.println("java:comp/env/timeZone=" + timeZone); } } catch (NamingException e) { throw new EJBException(e); } return getTimeZoneString(timeZone); //====================================================================== public void ejbCreate() { System.out.println("ZoneBean: ejbCreate()"); } public void ejbRemove() { System.out.println("ZoneBean: ejbRemove()"); } public void ejbActivate() { System.out.println("ZoneBean: ejbActivate()"); http://j2ee.ie.tu.koszalin.pl -36- 468 469 470 471 472 473 474 475 476 477 478 479 } public void ejbPassivate() { System.out.println("ZoneBean: ejbPassivate()"); } public void setSessionContext(SessionContext ctx) { System.out.println("ZoneBean: setEntityContext(ctx)"); context = ctx; } //====================================================================== } 5.0.2. Interfejs domowy - ZoneHome.java 480 481 482 483 484 485 486 487 488 489 package systemy.session.zone; import java.rmi.RemoteException; import javax.ejb.CreateException; import javax.ejb.EJBHome; public interface ZoneHome extends EJBHome { Zone create() throws RemoteException, CreateException; } 5.0.3. Interfejs zdalny - Zone.java 490 491 492 493 494 495 496 497 498 499 package systemy.session.zone; import java.rmi.RemoteException; import javax.ejb.EJBObject; public interface Zone extends EJBObject { //deklaracja wybranych metod biznesowych zdefiniowanych w klasie ZoneBean public String getPolandTimeZoneString() throws RemoteException; } 5.0.4. Interfejs lokalny-domowy ZoneLocalHome.java 500 501 502 503 504 505 506 507 508 package systemy.session.zone; import javax.ejb.CreateException; import javax.ejb.EJBLocalHome; public interface ZoneLocalHome extends EJBLocalHome { ZoneLocal create() throws CreateException; } 5.0.5. Interfejs lokalny - ZoneLocal.java 509 510 511 512 513 514 515 516 517 518 package systemy.session.zone; import javax.ejb.EJBLocalObject; public interface ZoneLocal extends EJBLocalObject { //deklaracja wybranych metod biznesowych zdefiniowanych w klasie ZoneBean public String getLocalTimeZoneString(); public String getContextTimeZoneString(); } http://j2ee.ie.tu.koszalin.pl -37- 5.0.6. Klasa komponentu SessionBean - ZoneFacadeBean.java 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 package systemy.session.zone; import java.util.Calendar; import java.util.GregorianCalendar; import java.util.TimeZone; import import import import import javax.ejb.EJBException; javax.ejb.SessionBean; javax.ejb.SessionContext; javax.naming.InitialContext; javax.naming.NamingException; public class ZoneBean implements SessionBean { private static final long serialVersionUID = 3L; private SessionContext context = null; //====================================================================== //metoda biznesowa getTimeZoneString //====================================================================== private String getTimeZoneString(String zone) { System.out.println("ZoneBean: getTimeZoneString()"); Calendar cal = new GregorianCalendar(TimeZone.getTimeZone(zone)); return cal.get(Calendar.HOUR_OF_DAY)+":"+cal.get(Calendar.MINUTE); } //====================================================================== //metoda biznesowa getLocalTimeString //====================================================================== public String getLocalTimeZoneString() { System.out.println("ZoneBean: getLocalTimeZoneString()"); Calendar cal = new GregorianCalendar(); return cal.get(Calendar.HOUR_OF_DAY)+":"+cal.get(Calendar.MINUTE); } //====================================================================== //metoda biznesowa getPolandTimeZoneString //====================================================================== public String getPolandTimeZoneString() { System.out.println("ZoneBean: getPolandZoneTimeString()"); return getTimeZoneString("Europe/Warsaw"); } //====================================================================== //metoda biznesowa getContextTimeZoneString //====================================================================== public String getContextTimeZoneString() { System.out.println("ZoneBean: getContextTimeZoneString()"); String timeZone = null; try { InitialContext initialContext = new InitialContext(); timeZone = (String) initialContext.lookup("java:comp/env/timeZone"); System.out.println("java:comp/env/timeZone="+timeZone); }catch(NamingException e){ throw new EJBException(e); } return getTimeZoneString(timeZone); } //====================================================================== public void ejbCreate() { System.out.println("ZoneBean: ejbCreate()"); } public void ejbRemove() { System.out.println("ZoneBean: ejbRemove()"); } public void ejbActivate() { System.out.println("ZoneBean: ejbActivate()"); } public void ejbPassivate() { System.out.println("ZoneBean: ejbPassivate()"); } public void setSessionContext(SessionContext ctx) { System.out.println("ZoneBean: setEntityContext(ctx)"); context = ctx; } } http://j2ee.ie.tu.koszalin.pl -38- 5.0.7. Interfejs domowy - ZoneFacadeHome.java 601 602 603 604 605 606 607 608 609 610 611 package systemy.session.zoneFacade; import java.rmi.RemoteException; import javax.ejb.CreateException; import javax.ejb.EJBHome; public interface ZoneFacadeHome extends EJBHome { ZoneFacade create() throws RemoteException, CreateException; } 5.0.8. Interfejs zdalny - ZoneFacade.java 612 613 614 615 616 617 618 619 620 621 622 623 624 package systemy.session.zoneFacade; import java.rmi.RemoteException; import javax.ejb.EJBObject; import javax.resource.ResourceException; public interface ZoneFacade extends EJBObject { //metody biznesowe ZoneFacade-->Zone public String getServerTimeZoneString() throws RemoteException, ResourceException; public String getApplicationTimeZoneString() throws RemoteException, ResourceException; } Rys. 35. Klasowy diagram UML komponentu ZoneBean Na diagramie z rysunku 35 przedstawiono klasę ZoneBean implementującą interfejs SessionBean. Klasa zawiera jedną metodę prywatną (getTimeZoneString) dostępną tylko wewnątz klasy oraz trzy publiczne metody biznesowe (getLocalTimeZoneString, getPolandTimeZoneString, getContextTimeZoneString), które mogą zostać zadeklarowane w interfejsie zdalnym i lokalnym. http://j2ee.ie.tu.koszalin.pl -39- Rys. 36. Klasowy diagram UML komponentu ZoneFacadeBean Komponenty ZoneFacadeBean, którego diagram klas przedstawiono na rysunku 36, jest “klientem” klasy komponentu ZoneBean. Ponieważ oba komponenty są tworzone w ramach jednej wirtualnej maszyny javy to do komunikacji kodu ZoneFacadeBean z komponentem ZoneBean wykorzystano jego interfejsy lokalne ZoneLocalHome i ZoneLocal. Rys. 37. Mapowanie klas komponentu sesyjnego ZoneBean http://j2ee.ie.tu.koszalin.pl -40- Rys. 38. Mapowanie klas komponentu sesyjnego ZoneFacadeBean Rys. 39. Mapowanie nazw JNDI dla komponentów ZoneBean i jego fasady ZoneFacadeBean http://j2ee.ie.tu.koszalin.pl -41- Rys. 40. Mapowanie refernecji komponentu ZoneBean dla klas komponentu ZoneFacadeBean http://j2ee.ie.tu.koszalin.pl -42- 5.0.9. Klasa klienta – ZoneClient.java 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 package systemy.client; import java.util.Properties; import javax.naming.Context; import javax.naming.InitialContext; import javax.rmi.PortableRemoteObject; import import import import systemy.session.zone.Zone; systemy.session.zone.ZoneHome; systemy.session.zoneFacade.ZoneFacade; systemy.session.zoneFacade.ZoneFacadeHome; public class ZoneClient { public static void main(String[] args) { Properties env = new Properties(); //J2EE RI 1.3.1 env.put("java.naming.factory.initial","com.sun.enterprise.naming.SerialInitContextFactory"); env.put("java.naming.provider.url", "iiop://localhost:1050"); //====================================================================== // bezpośrednia obsługa komponentu ZoneBean poprzez interfejsy zdalne // ZoneHome i Zone //====================================================================== try { Context ctx = new InitialContext(env); Object objref = ctx.lookup("ejb/Zone"); //nazwa JNDI ZoneHome home = (ZoneHome) PortableRemoteObject.narrow(objref,ZoneHome.class); Zone zone = home.create(); System.out.println(zone.getPolandTimeZoneString()+" <= Poland Time Zone"); } catch (Exception e) { System.err.println(e); } //====================================================================== // pośrednia obsługa komponentu ZoneBean poprzez interfejsy lokalne // ZoneLocalHome i ZoneLocal za pośrednictwem komponentu // ZoneFacadeBean z interfejsami zdalnymi ZoneFacadeHome i ZoneFacade //====================================================================== try { Context ctx = new InitialContext(env); Object objref = ctx.lookup("ejb/ZoneFacade"); //nazwa JNDI fasady ZoneFacadeHome home = (ZoneFacadeHome) PortableRemoteObject.narrow (objref,ZoneFacadeHome.class); ZoneFacade zoneFacade = home.create(); System.out.println(zoneFacade.getApplicationTimeZoneString()+" <= Application Time Zone"); System.out.println(zoneFacade.getServerTimeZoneString()+" <= Server Time Zone"); } } } catch (Exception e) { System.err.println(e); } //====================================================================== http://j2ee.ie.tu.koszalin.pl -43- 5.1. Wzorzec adaptera Tworząc klasy komponentów EJB programista jest zmuszony do implementacji dodatkowych metod wymaganych przez interfejs SessionBean, EntityBean lub MessageDrivenBean w zależności od typu komponentu. Aby usprawnić projektowanie stosuje się odpowiednie klasy adaptacyjne dla każdego z typów komponentów. Np. dla komponentów sesyjnych taka klasa będzie miała następującą postać: 5.1.1. Klasa adaptera SessionBeanAdapter.java 683 684 685 686 687 688 689 690 691 692 693 694 695 696 package project.adapter; import javax.ejb.SessionBean; import javax.ejb.SessionContext; public abstract class SessionBeanAdapter implements SessionBean { protected SessionContext context = null; public void setSessionContext(SessionContext ctx) { context = ctx;} public void ejbActivate() {} public void ejbPassivate() {} public void ejbCreate() {} public void ejbRemove() {} } Tak przygotowana klasa może być wykorzystana przez projektowany komponent sesyjny. 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 package project.session; import project.adapter.SessionBeanAdapter; ... public class FacadeBean extends SessionBeanAdapter { public void metodaBiznesowa1(...) { ... } public String metodaBiznesowa2(...) { ... } ... } //nadpisanie metody ejbCreate z klasy rodzica public void ejbCreate() { ... } Stosując ogólne zasady dziedziczenia uzyskujemy klasę naszego komponentu sesyjnego, który posiada metody biznesowe. W przypadku potrzeby wykorzystania jakiejś metody rodzica (w przykładzie z klasy SessinAdapterBean) wystarczy ją ponownie zdefiniować. http://j2ee.ie.tu.koszalin.pl -44- 5.2. Parametryzacja komponentów EJB W przykładowej metodzie test() komponentu FacadeBean zastosowano pobieranie obiektu String “test”, którego zawartość była przypisana na stałe. Kontener EJB umożliwia definiowanie właściwości dla komponentów EJB w ten sposób, że ich zmiana nie wymaga ponownej kompilacji kodu java. 5.2.1. Fragment kodu z klasy FacadeBean.java 720 721 722 723 724 725 726 727 //====================================================================== //metoda biznesowa test() //====================================================================== public String test() { } System.out.println("[INFO] FacadeBean: test()"); return "test"; Zmodyfikowany kod wyszukuje lokalnie zawartości klucza “message”. Nic nie stoi na przeszkodzie , aby nazwa była pobierana zdalnie z innego systemu , ponieważ wszystko oparte jest o usługę JNDI. 5.2.2. Zmodyfikowany fragment kodu z klasy FacadeBean.java 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 //====================================================================== //metoda biznesowa test() //====================================================================== public String test() { System.out.println("[INFO] FacadeBean: test()"); String message = null; try { InitialContext initialContext = new InitialContext(); message = (String) initialContext.lookup("java:comp/env/message"); }catch(NamingException e){ throw new EJBException(e); } return message; } Ostatnim etapem jest ustawienie klucza i wartości dla naszej aplikacji EAR. Rys. 41. Ustawianie właściwości dla komponentu FacadeBean – zakładka Env. Entries http://j2ee.ie.tu.koszalin.pl -45- 5.3. Aplikacja webowa WAR Opis w przygotowaniu...iu... http://j2ee.ie.tu.koszalin.pl -46- VI.Komponenty Entity EJB 6.1. Utrwalanie danych 6.2. Komponenty CMP 2.X Uwaga! Dla komponentów Entity EJB typu CPM 2.0 (zarządzanych trwałością przez kontener) należy budować klasy abstrakcyjne - abstract. 6.2.1. Klasa komponetnu Entity EJB (CMP) – UserBean.java 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 package systemy.entity; import javax.ejb.CreateException; import javax.ejb.EntityBean; import javax.ejb.EntityContext; public abstract class UserBean implements EntityBean { private EntityContext ctx; public abstract void setLogin(String login); public abstract String getLogin(); public abstract void setPass(String pass); public abstract String getPass(); public abstract void setEmail(String email); public abstract String getEmail(); public void setEntityContext(EntityContext ctx) { this.ctx = ctx; } public void unsetEntityContext() { this.ctx = null; } public String ejbCreate(String login, String pass, String email) throws CreateException { setLogin(login); setPass(pass); setEmail(email); } return null; public void ejbPostCreate(String login, String pass, String email) { } } public public public public public void void void void void ejbRemove() { } ejbActivate() { } ejbPassivate() { } ejbLoad() { } ejbStore() { } 6.2.2. Interfejs zdalny - User.java 784 785 786 787 788 789 790 791 792 793 794 795 796 797 package systemy.entity; import java.rmi.RemoteException; import javax.ejb.EJBObject; public interface User extends EJBObject { public String getLogin() throws RemoteException; public void setLogin(String login) throws RemoteException; public String getPass() throws RemoteException; public void setPass(String pass) throws RemoteException; http://j2ee.ie.tu.koszalin.pl -47- 798 799 800 801 public String getEmail() throws RemoteException; public void setEmail(String email) throws RemoteException; } 6.2.3. Interfejs domowy - UserHome.java 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 package systemy.entity; import java.rmi.RemoteException; import javax.ejb.CreateException; import javax.ejb.EJBHome; import javax.ejb.FinderException; public interface UserHome extends EJBHome { public User create(String login, String pass, String email) throws RemoteException,CreateException; public User findByPrimaryKey(String primaryKey) throws FinderException, RemoteException; } Info! W pierwszej specyfikacji przewidziano jedynie interfejs zdalny i domowy dla komponentów EJB. Ze względów wydajnościowych w kolejnych wersjach J2EE zastosowano dodatkowo dwa interfejsy lokalne: lokalny i domowy-lokalny. 6.2.4. Interfejs lokalny - UserLocal.java 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 package systemy.entity; import javax.ejb.EJBLocalObject; public interface UserLocal extends EJBLocalObject { public String getLogin(); public String getPass(); public void setPass(String pass); public String getEmail(); public void setEmail(String email); } 6.2.5. Interfejs lokalny-domowy - UserLocalHome.java 832 833 834 835 836 837 838 839 840 841 842 package systemy.entity; import javax.ejb.CreateException; import javax.ejb.EJBLocalHome; import javax.ejb.FinderException; public interface UserLocalHome extends EJBLocalHome { } public UserLocal create(String login, String pass, String email) throws CreateException; public UserLocal findByPrimaryKey(String primaryKey) throws FinderException; http://j2ee.ie.tu.koszalin.pl -48- 6.3. GUID Podczas projektowania komponentów Entity Bean występuje problem ustalenia wartości klucza głównego wstawianych danych. Każdy obiekt Entity musi być unikatowy, a jak wiadomo ich utrwalanie jest realizowane poprzez wstawienie nowego rekordu do bazy danych. Skąd jednak wziąć wartość klucza głównego? Jest kilka możliwości. Pierwsza i najprostsza to wykorzystanie pewnych unikalnych danych biznesowych np. dla komponentu opisującego podatnika w urzędzie skarbowym takim elementem może być jego NIP. Co jednak zrobić, gdy nie możemy określić takiego parametru? Można skorzystać z auto-inkrementacji klucza głównego danej bazy danych. Niestety taka obsługa nie jest uwzględniana w specyfikacji J2EE i każdy serwer J2EE realizuje to w swój sposób poprzez dodatkowe wpisy we własnych deskryptorach. Innym sposobem jest stworzenie własnego komponentu, który będzie sam zwiększał swój licznik. Wiąże się to jednak z problemami synchronizacji wielu komponentów EJB, co automatycznie spowalnia pracę systemu tym bardziej, że komponenty mogą istnieć na innych komputerach. Sposobem uniknięcia wyżej wymienionego problemu jest takie wygenerowanie identyfikatora, aby był on zależny od takich parametrów jak: • czas, • adres komputera w sieci jako adres IP, • typ obiektu na rzecz którego generuje się identyfikator. Ostatni element ma za zadanie wymusić unikalność wartości identyfikatora w przypadku, gdy w tym samym czasie, na tym samym komputerze w dwóch różnych wątkach nastąpi próba generacji identyfikatora. 6.3.1. Klasa Guid.java 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 package project.util; // Kod klasy wygenerowany przy użyciu XDolcet public class Guid { private static String hexServerIP = null; // initialise the secure random instance private static final java.security.SecureRandom seeder = new java.security.SecureRandom(); /** * A 32 byte GUID generator (Globally Unique ID). These artificial keys * SHOULD <strong>NOT </strong> be seen by the user, not even touched by the * DBA but with very rare exceptions, just manipulated by the database and * the programs. * * Usage: Add an id field (type java.lang.String) to your EJB, and add * setId(XXXUtil.generateGUID(this)); to the ejbCreate method. */ public static final String generateGUID(Object o) { StringBuffer tmpBuffer = new StringBuffer(16); if (hexServerIP == null) { java.net.InetAddress localInetAddress = null; try { // get the inet address localInetAddress = java.net.InetAddress.getLocalHost(); } catch (java.net.UnknownHostException uhe) { System.err.println("Could not get the local IP address using InetAddress.getLocalHost()!"); http://j2ee.ie.tu.koszalin.pl -49- 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 // todo: find better way to get around this... uhe.printStackTrace(); return null; } byte serverIP[] = localInetAddress.getAddress(); hexServerIP = hexFormat(getInt(serverIP), 8); } String hashcode = hexFormat(System.identityHashCode(o), 8); tmpBuffer.append(hexServerIP); tmpBuffer.append(hashcode); long timeNow = System.currentTimeMillis(); int timeLow = (int) timeNow & 0xFFFFFFFF; int node = seeder.nextInt(); } StringBuffer guid = new StringBuffer(32); guid.append(hexFormat(timeLow, 8)); guid.append(tmpBuffer.toString()); guid.append(hexFormat(node, 8)); return guid.toString(); private static int getInt(byte bytes[]) { int i = 0; int j = 24; for (int k = 0; j >= 0; k++) { int l = bytes[k] & 0xff; i += l << j; j -= 8; } return i; } private static String hexFormat(int i, int j) { String s = Integer.toHexString(i); return padHex(s, j) + s; } private static String padHex(String s, int i) { StringBuffer tmpBuffer = new StringBuffer(); if (s.length() < i) { for (int j = 0; j < i - s.length(); j++) { tmpBuffer.append('0'); } } return tmpBuffer.toString(); } } http://j2ee.ie.tu.koszalin.pl -50- 6.4. Wzorzec Obiektu Transferu Danych Wielokrotne wywoływanie kolejnych metod setXXX i getXXX dla ustalania zawartości poszczególnych pól komponentów Entity EJB jest zajęciem czasochłonnym. Ma to szczególne znaczenie przy połączeniach zdalnych, które w takim przypadku są nadmiernie obciążane. Sposobem na to jest stworzenie nowej klasy zawierającej wszystkie wymagane pola i zbiorczy transfer wszystkich danych jednego obiektu przy pomocy jednego połączenia. Zaprojektowana klasa UserData jest bardzo pomocna przy przenoszeniu danych pomiędzy warstwą biznesową (komponenty EJB), a warstwą prezentacji (kod klienta, aplikacja webowa). 6.4.1. Klasa do transferu danych użytkownika - UserData.java 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 package systemy.entity; import java.io.Serializable; public class UserData implements Serializable { private String login; private String password; private String email; public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public String getLogin() { return login; } public void setLogin(String login) { this.login = login; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public UserData() { } public UserData(String login, String password, String email) { setLogin(login); setPassword(password); setEmail(email); } public String toString() { return "login="+getLogin()+" password="+getPassword()+" email="+getEmail(); } public boolean equals(Object o) { if(o instanceof UserData ) { UserData test = (UserData)o; boolean equals = true; if(login == null){ equals = equals && (test.login == null); } else { equals = equals && login.equals(test.login);} if(password == null){ equals = equals && (test.password == null); } else { equals = equals && password.equals(test.password); } if(email == null){ equals = equals && (test.email == null); } else { equals = equals && email.equals(test.email); } return equals; http://j2ee.ie.tu.koszalin.pl -51- 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 } } else { return false; } public int hashCode() { int result = 17; } } result result result return = 37*result + ((login != null) ? login.hashCode() : 0); = 37*result + ((password != null) ? password.hashCode() : 0); = 37*result + ((email != null) ? email.hashCode() : 0); result; Do klasy dodano konstruktor z trzema paramtrami (login, password, email) ułatwiający wypełnianie obiektów UserData podczas ich tworzenia. Dodatkowo do klasy UserData można dodać metody pomocnicze. Przykładem takich metod może być walidacja przechowywanych danych, klonowanie, czy też porównywanie zawartości obiektów pomocne np. przy ich sortowaniu w listach. Weryfikacja (validate) w procesie wstawiania i/lub modyfikacji danych jest bardzo ważnym czynnikiem. Proces ten realizuje się na ogół po stronie serwera w aplikacjach webowych wspomaganych dodatkowo skryptami java w przeglądarkach klienta. Jeśli z naszego systemy mają korzystać również w sposób bezpośredni inne aplikacje klienta to proces weryfikacji należy umieścić albo w kodzie delegata biznesowego, ale najlepsze rozwiązanie to fasada sesji – delegat biznesowy może być umieszczony poza serwerem i możemy utracić kontrolę nad jego kodem. http://j2ee.ie.tu.koszalin.pl -52- 6.5. Wzorzec Facade Aby zaprezentować sposób budowania fasady zaprojektowano dwie klasy. http://j2ee.ie.tu.koszalin.pl -53- 6.6. Wzorzec Lokalizatora Usług Wzorzec Lokalizatora Usług został już omówiony podczas opisu tworzenia kodu klient komunikującego się z komponentami EJB. Komponenty EJB zawarte w kontenerze EJB także mogą pełnić funkcję klienta dla innych komponentów EJB lokalnych danego kontenera lub zdalnych na innym serwerze J2EE. http://j2ee.ie.tu.koszalin.pl -54- 6.7. Relacje CMR 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 <?xml version="1.0" encoding="UTF-8"?> ... <ejb-jar> ... <relationships> <ejb-relation> <ejb-relationship-role> <ejb-relationship-role-name>FirmaBean</ejb-relationship-role-name> <multiplicity>One</multiplicity> <relationship-role-source> <ejb-name>FirmaBean</ejb-name> </relationship-role-source> <cmr-field> <cmr-field-name>pracownicy</cmr-field-name> <cmr-field-type>java.util.Collection</cmr-field-type> </cmr-field> </ejb-relationship-role> <ejb-relationship-role> <ejb-relationship-role-name>PracownikBean</ejb-relationship-role-name> <multiplicity>Many</multiplicity> <cascade-delete /> <relationship-role-source> <ejb-name>PracownikBean</ejb-name> </relationship-role-source> <cmr-field> <cmr-field-name>firma</cmr-field-name> </cmr-field> </ejb-relationship-role> </ejb-relation> </relationships> ... </ejb-jar> Rys. 42. Definiowanie relacji (rysunek do wymiany) http://j2ee.ie.tu.koszalin.pl -55- 6.8. Metody Finder i Select 6.9. EJB-QL 6.9.1. Składnia zapytania EJB-QL EJB QL ::= select_clause from_clause [ where_clause] Where_clause ::= WHERE conditional_expression SELECT [DISTINCT] {single_valued_path_expression | OBJECT (identification_variable) abstract-schema-name ejb-name Lista identyfikatorów: • SELECT, FROM, WHERE, DISTINCT,IN, AS, OF • BETWEEN, IS, LIKE • TRUE, FALSE • NOT, AND, OR • NULL, UNKNOWN, MEMBER, EMPTY, OBJECT 6.9.2. Przykładowe metody findXXX Metody findXxx będą wywoływane przez kod klienta dlatego ich deklaracje należy umieszczać wewnątrz interfejsów domowych. Metody wyszukiwania nie są realizowane ma rzecz pojedynczej instancji komponentu, ale na całej grupie komponentów. Z tego względu deklaracja metody wyszukiwania znajduje się w interfejsie domowym, w tym samym w którym zadeklarowano metody create. 1040 public Order findByPrimaryKey(int orderId) throws FinderException; 1041 public Order findBiggestOrder() throws FinderException; 1042 public java.util.Collection findAllOrders(String supplierName) throws FinderException; 6.9.3. Przykładowe metody selectXxx i selectXxxInEntity Metody selectXxx dotyczą wewnętrznego wywoływania metod w komponencie Entity dlatego deklaruje się je wewnątrz abstrakcyjnych klasy komponentów EntityBean. 1043 <query> 1044 <description></description> 1045 <query-method> 1046 <method-name>findByAll</method-name> 1047 <method-params /> http://j2ee.ie.tu.koszalin.pl -56- 1048 </query-method> 1049 <ejb-ql>SELECT Object(o) from PracownikBean as o</ejb-ql> 1050 </query> Info! W sieci można odnaleźć ciekawy artykuł na temat EJB-QL. Znajduje się on na serwerze OnJava.com pod adresem http://www.onjava.com/lpt/a/1152 http://j2ee.ie.tu.koszalin.pl -57- VII.Transakcje 7.1. Model ACID w komponentach EJB Opis w przygotowaniu... 7.2. Wycofanie transakcji Opis w przygotowaniu... Opracowanie komponentu Entiry KontoBean oraz jego komponentu fasady KontoFacadeBean zawierającego metody transferu gotówki (w fasadzie zastosować dwie kopie metod z deklaratywną i programową obsługą transakcji – symulacja metod przy wypłacie gotówki i wywoływanie metody setRolbackTransaction przy zerowym stanie konta ). Do tego wszystkiego trzeci sposób - prosty klient SimpleTransactionClient z obsługą transakcji poza komponentem EJB. W perspektywie.... Można umieścić porównanie wydajności różnych ustawień transakcji przy ich testowaniu – np. narzędziem JMETER przez WWW. 7.3. Mapowanie metod komponentów EJB Podczas tworzenia komponentów EJB projektant ma możliwość zadeklarowania wymaganego stanu transakcji dla danej metody. http://j2ee.ie.tu.koszalin.pl -58- 7.4. Poziomy izolacji transakcji Poziom izolacji transakcji Dirty NonPhantom repeatable TRANSACTION_READ_UNCOMMITTED brak izolacji Tak Tak Tak TRANSACTION_READ_COMMITTED częściowa izolacja Nie Tak Tak TRANSACTION_REPEATABLE_READ częściowa izolacja Nie Nie Tak TRANSACTION_SERIALIZABLE Pełna izolacja Nie Nie Nie Wymagany stan jest opisywany przy pomocy określonego atrybutu: • NotSupported • Supports • Required • RequiresNew • Mandatory • Never Typ zarządzania transakcjami: • Bean • Container 7.4.1. Fragment desktyptora - ejb-jar.xml 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 <?xml version="1.0" encoding="UTF-8"?> ... <ejb-jar> ... <assembly-descriptor> <container-transaction> <method> <ejb-name>FacadeBean</ejb-name> <method-name>*</method-name> </method> <trans-attribute>Required</trans-attribute> </container-transaction> <container-transaction> <method> <ejb-name>PracownikBean</ejb-name> <method-name>*</method-name> </method> <trans-attribute>Required</trans-attribute> </container-transaction> <container-transaction> <method> <ejb-name>FirmaBean</ejb-name> <method-name>*</method-name> </method> <trans-attribute>Required</trans-attribute> </container-transaction> </assembly-descriptor> </ejb-jar> http://j2ee.ie.tu.koszalin.pl -59- VIII.JNDI 8.1. Interfejs Context Podczas tworzenia kodu klienta w przykładzie SimpleClient.java wykorzystano interfejs kontekstu. 8.1.1. Fragment pliku – SimpleClient.java 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 Properties env = new Properties(); //J2EE RI 1.3.1 env.put("java.naming.factory.initial","com.sun.jndi.cosnaming.CNCtxFactory"); env.put("java.naming.provider.url", "iiop://localhost:1050"); try { Context ctx = new InitialContext(env); Object objref = ctx.lookup("ejb/Simple"); //nazwa JNDI ... } catch (Exception e) { System.err.println(e); } http://j2ee.ie.tu.koszalin.pl -60- Info! Interfejs Context jak i cały pakiet javax.naming.* do obsługi JNDI należy do standardowej specyfikacji javy (biblioteka rt.jar z JRE System Library). 1102 1103 1104 1105 1106 env.put("java.naming.factory.initial","com.sun.jndi.cosnaming.CNCtxFactory"); env.put("java.naming.provider.url", "iiop://localhost:1050"); env.put(Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.cosnaming.CNCtxFactory"); env.put(Context.PROVIDER_URL, "iiop://localhost:1050"); 8.2. Interfejs DirContext Opis w przygotowaniu... http://j2ee.ie.tu.koszalin.pl -61- IX.Ldap Serwer OpenLdap jest tworzony głównie na systemach Unixowych i istnieje prawie w każdej dystrybucji systemu Linux w postaci źródeł, czy też spakowanych paczek. Do pracy serwera w systemie Windows należy użyć skompilowanej wersji. W ramach zajęć będziemy korzystać z takiej wersji. Serwer posiada plik konfiguracyjny, który należy odpowiednio zmodyfikować i dostosować do określonej lokalizacji na dysku. 9.1. Konfiguracja serwera OpenLdap 9.1.1. Plik konfiguracyjny serwera OpenLDAP - slap.conf 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 # $OpenLDAP: pkg/ldap/servers/slapd/slapd.conf include include include include c:/java/openLDAP/schema/core.schema c:/java/openLDAP/schema/cosine.schema c:/java/openLDAP/schema/inetorgperson.schema c:/java/openLDAP/schema/java.schema ####################################################################### # ldbm database definitions ####################################################################### database suffix rootdn rootpw directory ldbm "o=JNDITutorial" "cn=admin,o=JNDITutorial" portal c:/java/openLDAP/ldbm/ldbm_jndi Aby uruchomić serwer OpenLDAP należy wywołać polecenie slapd.exe. Serwer nasłuchuje na porcie 389. http://j2ee.ie.tu.koszalin.pl -62- 9.2. Aplikacja klienta - LdapBrowser Rys. 43. Aplikacja klienta – LdapBrowser (java) http://j2ee.ie.tu.koszalin.pl -63- 9.3. Komunikacja z serwerem 9.3.1. Przykład prostego klienta – LdapClient.java 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 package systemy.client; import java.util.Properties; import javax.naming.Context; import javax.naming.directory.Attributes; import javax.naming.directory.DirContext; import javax.naming.directory.InitialDirContext; public class LdapClient { public static void main(String[] args) { Properties env = new Properties(); env.put(Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.ldap.LdapCtxFactory"); env.put(Context.PROVIDER_URL, "ldap://localhost:389/o=JNDITutorial"); env.put(Context.SECURITY_PRINCIPAL, "cn=admin,o=JNDITutorial"); env.put(Context.SECURITY_CREDENTIALS, "portal"); try { DirContext ctx = new InitialDirContext(env); DirContext ctx2 = (DirContext)ctx.lookup("ou=people"); System.out.println(ctx2.getNameInNamespace()); Attributes attrs = ctx2.getAttributes(""); System.out.println(attrs); } } } catch (Exception e) { System.err.println(e); } http://j2ee.ie.tu.koszalin.pl -64- X.Komunikaty JMS 10.1. Konfiguracja JMS na serwerze J2EE RI Rys. 44. Konfiguracja fabryk JMS Rys. 45. Konfiguracja kolejek JMS typu Topic i Queue http://j2ee.ie.tu.koszalin.pl -65- 10.2. Komponent MDB Podstawową właściwością odróżniającą komponenty MDB od Session i entity jest brak interfejsów. Komponent zbudowany jest jedynie z klasy rozszerzającej interfejs MessageDrivenBean. Powodem braku interfejsów jest brak możliwości bezpośredniej komunikacji z komponentem zewnętrznych klientów. Klientem komponentu jest kolejka komunkatów JMS, której mapowanie odbywa się przy konfiguracji EAR na serwerze. 10.2.1. Klasa wzorca adaptera dla MDB – MessageDrivenBean.java 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 package project.adapter; import javax.ejb.MessageDrivenBean; import javax.ejb.MessageDrivenContext; public abstract class MessageBeanAdapter implements MessageDrivenBean { protected MessageDrivenContext context = null; } public void setMessageDrivenContext(MessageDrivenContext ctx) { context = ctx; } public void ejbCreate(){} public void ejbRemove(){} 10.2.2. Klasa komponentu MDB – LogBean.java 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 package project.message; import import import import import javax.jms.JMSException; javax.jms.Message; javax.jms.MessageListener; javax.jms.ObjectMessage; javax.jms.TextMessage; import project.adapter.MessageBeanAdapter; public class LogBean extends MessageBeanAdapter implements MessageListener { private static final long serialVersionUID = 5L; public void onMessage(Message msg) { System.out.println("-------------------------------------------------"); // pobranie parametru z komunikatu - przydatne do dalszego przekierowania try { System.out.println("parametr info: "+msg.getStringProperty("info")); } catch(JMSException e) { } if(msg instanceof TextMessage) { TextMessage tm = (TextMessage)msg; try { } String text = tm.getText(); System.out.println("Komunikat TextMessage: "+text); } catch(JMSException e) { System.err.println(e); } if(msg instanceof ObjectMessage) { ObjectMessage tm = (ObjectMessage)msg; try { } } } Object obj = tm.getObject(); System.out.println("Komunikat ObjectMessage: "+obj.getClass()+" "+obj); } catch(JMSException e) { System.err.println(e); } http://j2ee.ie.tu.koszalin.pl -66- Rys. 46. Mapowanie komponentu MDB Podczas mapowania komponentu MDB można zauważyć brak interfejsów zdalnych, czy też lokalnych. Związane jest to z tym, że kod klasy komponentu nie jest bezpośrednio wywoływany przez klienta. Metoda onMessage jest aktywowana przez kolejkę JMS konfigurowana przez serwer J2EE. Rys. 47. Mapowanie kolejki i fabryki komponentu MDB Razem z serwerem j2EE RI 1.3.1 jest uruchamiany serwer JMS. Jest in silnie związany z serwerem baz danych ponieważ natura komunikatów JMS określa ich asynchroniczność. http://j2ee.ie.tu.koszalin.pl -67- Komunikat może zostać wysłany do kolejki z której będzie odebrany w późniejszym czasie. Z tego względu treść komunikatu musi zostać czasowo utrwalona. 10.3. Prosty klient wysyłający komunikaty JMS Przykładowy kod realizuje wysłanie tekstowego komunikatu do kolejki jms/Topic skonfigurowanej przez działający serwer J2EE. Kolejka została udostępniona lokalnie i kod działa na tym samym komputerze (bez rozproszenia). W przypadku wysyłania komunikatów JMS przy pomocy IIOP zdalnie należy najpierw odnaleźć kolejkę na serwerze JNDI. 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 package project.client; import java.util.Date; import import import import import import import import import import javax.jms.ObjectMessage; javax.jms.Session; javax.jms.TextMessage; javax.jms.Topic; javax.jms.TopicConnection; javax.jms.TopicConnectionFactory; javax.jms.TopicPublisher; javax.jms.TopicSession; javax.naming.Context; javax.naming.InitialContext; public class JmsClient { public static void main(String[] args) { try { //inicjalizacja kontekstu Context ctx = new InitialContext(); TopicConnectionFactory factory = (TopicConnectionFactory) ctx.lookup ("jms/TopicConnectionFactory"); //nazwa JNDI fabryki JMS 1252 TopicConnection connection = factory.createTopicConnection(); 1253 TopicSession topicSession = connection.createTopicSession (false,Session.AUTO_ACKNOWLEDGE); 1254 Topic topic = (Topic) ctx.lookup("jms/Topic"); 1255 1256 TopicPublisher publisher = topicSession.createPublisher(topic); 1257 1258 //przesyłanie komunikatu w postaci tekstu 1259 TextMessage textMsg = topicSession.createTextMessage(); 1260 1261 textMsg.setStringProperty("info","Przesylka w postaci komunikatu TextMessage"); textMsg.setText("Tresc tekstowa komunikatu... "); 1262 1263 1264 1265 1266 1267 1268 publisher.publish(textMsg); System.out.println("Wysłano komunikat TextMessage --> "+textMsg); 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 } 1279 } publisher.publish(objMsg); System.out.println("Wysłano komunikat ObjectMessage --> "+objMsg); //przesyłanie komunikatu w postaci obiektu ObjectMessage objMsg = topicSession.createObjectMessage(); objMsg.setStringProperty("info","Przesylka w postaci komunikatu ObjectMessage"); objMsg.setObject(new Date()); connection.close(); } catch (Exception e) { System.err.println(e); return; } http://j2ee.ie.tu.koszalin.pl -68- Po wykonaniu kodu na konsoli klienta zostanie wyświetlony komunikat, którego zawartość została przesłana do kolejki JMS. Java(TM) Message Service 1.0.2 Reference Implementation (build b14) Wysłano komunikat TextMessage --> ID:_krypton-by6ksqw_1104758069097_36.1.1.1: Tresc tekstowa komunikatu... Wysłano komunikat ObjectMessage --> ID:_krypton-by6ksqw_1104758069097_36.1.1.2: contains a java.util.Date Przy odpowiednio skonfigurowanym na serwerze komponencie MDB jego metoda onMessage przejmuje przekazany komunikat i wyświetla jego zawartość na konsoli serwera. Deploying message driven bean LogBean, consuming from jms/Topic Binding name:`java:comp/env/ejb/Facade` Created Context:/project Application project deployed. ------------------------------------------------parametr info: Przesylka w postaci komunikatu TextMessage Komunikat TextMessage: Tresc tekstowa komunikatu... ------------------------------------------------parametr info: Przesylka w postaci komunikatu ObjectMessage Komunikat ObjectMessage: class java.util.Date Mon Jan 03 14:14:29 CET 2005 Rys. 48. Konsola serwera J2EE z treścią odebranych komunikatów JMS Na konsoli serwera J2EE podczas rozmieszczania aplikacji pojawiają się informacje o wiązaniach nazw JNDI poszczególnych komponentów EJB. Jest także informacja o konsumowaniu komunikatów z kolejki jms/Topic przez komponent MDB LogBean. W kodzie klienta, jak i w kodzie odbiorczym komponentu MDB zastosowano dwa typy komunikatów. Tekstowy TextMessage oraz do przesyłania zserializowanych obiektów ObjectMessage. Dodatkowo dodano ustawianie właściwości komunikatów. W przykładzie zastosowano właściwość typu String o nazwie info. Kod klienta ustawia właściwość, a komponent MDB ją pobiera z komunikatu. Kod zawarty w klasie JmsClient.java można równie dobrze umieścić w aplikacji webowej jaki część servletu, czy też strony jsp. 10.4. ServiceLocator – rozszerzenie wyszukiwania o kolejki JMS Podobnie jak z wzorcem wyszukiwania usług dla komponentów EJB można ten wzorzec stosować dla innych typów usług. Idea stosowania jest taka sama w każdym przypadku i polega na odciążeniu kodu klienta od technicznych znajomości związanych z JNDI, EJB, JMS, Web Services czy też innej technologii. http://j2ee.ie.tu.koszalin.pl -69- XI.Web Services W specyfikacji J2EE 1.3 nie uwzględniono jeszcze nowej, szybo rozwijającej się technologii Web Services opartej o tzw. architekturę opartą o serwisy (SOA). W nowej specyfikacji J2EE 1.4 usługi zostały już zintegrowane z warstwą kontenera EJB. Przy projektach wykorzystujących starszą specyfikację należy wykorzystać kod pośredni w postaci servletu. Oczekuje on na żądania klienta www, przetwarza otrzymaną informację w postaci koperty SOAP i na jej podstawie wywołuje metody wraz z ich parametrami klas java. Po otrzymaniu wyniku dokonuje ponownej translacji, ale w przeciwną stronę. Wynik działania metod java jest pakowany w kopertę SOAP i odesłany klientowi w formacie XML. Klient otrzymuje kopertę SOAP i przetwarza jej zawartość przy pomocy własnych mechanizmów, klient nie musi być realizowany w technologii java, a jedynie musi rozumieć gramatykę schematu dokumentów SOAP. Dodatkowo w celu informacji klientów o działaniu danej usługi serwer z naszym serwerem powinien udostępniać XML'owy dokument WSDL. Całość procesu obsługi usług Web Service w technologii java wspiera projekt Apaceh – AXIS. 11.1. AXIS Projekt Axis jest serwletem, który umieszczony w kontenerze web'owym (aplikacji WAR) umożliwia konwersję klas javy udostępnianych w postaci pliku źródłowego ze zmienionym rozszerzeniem *.jws. Dla wszystkich plików JWS serwlet umożliwia automatyczną generację dokumentów WSDL. 1280 public class Simple { 1281 1282 public String info() { 1283 return "info: wywołanie metody usługi sieciowej Simple.jws"; 1284 } 1285 } http://j2ee.ie.tu.koszalin.pl -70- XII.Autentykacja i Autoryzacja 12.1. Role użytkowników Rys. 49. Blokada metody test() komponentu SimbleBean (zakładka Security) Po ustawieniu blokady (opcja No Users) metody testowej komponentu EJB klient otrzymuje wyjątek braku zdalnego dostępu RMI - AccessException. java.rmi.AccessException: CORBA NO_PERMISSION 9998 Maybe; nested exception is: org.omg.CORBA.NO_PERMISSION: vmcid: 0x2000 minor code: 1806 completed: Maybe Rys. 50. Przypisanie roli project metodzie test() komponentu SimbleBean (zakładka Security) http://j2ee.ie.tu.koszalin.pl -71- 12.2. Komunikacja SSL Opis w przygotowaniu... http://j2ee.ie.tu.koszalin.pl -72- XIII.Testy jednostkowe Testowanie można podzielić na kilka rodzajów: • testowanie logiki – przy pomocy atrap – Mock Object; • testowanie integracyjne – np. przy pomocy Cactusa działającego w rzeczywistym kontenerze – kontener WEB lub EJB na serwerze J2EE; • testy funkcjonalne – np. HttpUnit testujące funkcjonowanie aplikacji przy wykorzystaniu żądań i odpowiedzi serwera. 13.1. JUnit Kluczowym elementem tworzenia projektu jest przeprowadzanie testów. Wielokrotne wykonywanie tych samych czynności wymusiło stworzenie narzędzi wspomagajacych testowanie. W ten sposób powstały testy jednostkowe stosowane w różnych jezykach (java, perl, python, c++, czy też c#). Jednostkowość polega na wydzieleniu elementarnych operacji, które mogą wykonywać się niezależnie od innych. Wynik testu jest diagnozowany na podstawie wywoływania metod asercji. Poprawne wykonanie wszystkich metod danego testu powoduje, że dany kod testowy wykonał się prawidłowo. 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 package systemy.client; import java.util.Properties; import javax.naming.Context; import javax.naming.InitialContext; import javax.rmi.PortableRemoteObject; import systemy.session.Facade; import systemy.session.FacadeHome; import junit.framework.TestCase; public class TestClient extends TestCase { Context ctx; protected void setUp() throws Exception { Properties env = new Properties(); //J2EE RI 1.3.1 env.put("java.naming.factory.initial","com.sun.enterprise.naming.SerialInitContextFactory"); env.put("java.naming.provider.url", "iiop://localhost:1050"); try { ctx = new InitialContext(env); } catch (Exception e) { System.err.println(e); } } //================================================================ public void testFacade() throws Exception { Object objref = ctx.lookup("ejb/FacadeBean"); //nazwa JNDI FacadeHome home = (FacadeHome) PortableRemoteObject.narrow(objref,FacadeHome.class); Facade facade = home.create(); String result = facade.test(); System.out.println(result); http://j2ee.ie.tu.koszalin.pl -73- 1327 assertEquals(result,"test"); 1328 } 1329 //================================================================ 1330 1331 protected void tearDown() throws Exception { } 1332 1333 public static void main(String[] args) { 1334 1335 junit.swingui.TestRunner.run(TestClient.class); 1336 //junit.awtui.TestRunner.run(TestClient.class); 1337 } 1338 } Rys. 51. Klient biblioteki JUnit Stosowanie testów jednostkowych ułatwia wprowadzanie zmian w projekcie. Optymalizując kod można szybciej sprawdzać jego poprawność poprzez wykonanie testów. Ma to kluczowe znaczenie przy coraz bardziej złożonych projektach. Grupowanie testów – w przygotowaniu. http://j2ee.ie.tu.koszalin.pl -74- 13.2. Projekt Cactus – osadzanie testów jednostkowych na serwerze J2EE Opis w przygotowaniu... http://j2ee.ie.tu.koszalin.pl -75- XIV.Komponent?! 14.1. Projekt CUBA Projektowanie komponentowe stawia za cel tworzenie kodu wielokrotnego użytku. Projekt CUBA umożliwia automatyzację procesu tworzenia dodatkowej infrastruktury do zaprojektowanego komponentu w zależności od typu komponentu i kontenera w jakim będzie egzystował. Dla zaprojektowanej klasy i interfejsu metod, które maja zostać udostępnione na zewnątrz projekt CUBA umożliwia automatyczne stworzenie klas i deskryptorów w zależności od typu komponentu wynikowego. Projektant ma możliwość generacji komponentów zdalnych obsługiwanych przy pomocy JNDI i RMI, komponentów EJB wymagających kontenera EJB, czy też usług sieciowych Web Services opartych o pakiet AXIS. java cuba.util.codegen.AdapterGenerator No adapter type specified Usage: AdapterGenerator <options> <component descriptor> or: AdapterGenerator <options> -> reads descriptor vom CLASSPATH Options are: -h this help message -a create a Web-Service-Adapter -e create EJB adapter -w create wired adapter -c create package structure -d <dir> output directory -x <class> XML parser to use (default cuba.wired.ddread.XmlParserXerces) http://j2ee.ie.tu.koszalin.pl -76- XV.Log4j 15.1. Poziomy rejestracji logów DEBUG ← INFO ← WARN ← ERROR ← FATAL (ALL) 1339 1340 1341 1342 1343 log.debug("DEBUG..."); log.info("INFO..."); log.warn("WARN..."); log.error("ERROR..."); log.fatal("FATAL..."); 15.2. Przekierowanie logów • • • • • • • console files GUI components remote socket JMS NT Events Logger SysLog (Unix) 15.3. Definiowanie wzorca przykłady: %d{ABSOLUTE} %-5p [%c{1}] %m%n %r [%t] %-5p %c - %m%n 176 [main] INFO org.foo.Bar - Located nearest gas station. %r czas w milisekundach od startu programu %t nazwa metody the number of milliseconds elapsed since the start of the program the thread making the log request the level of the log statement the name of the logger associated with the log request the '-' is the message of the statement. http://j2ee.ie.tu.koszalin.pl -77- Projekt log4j dostępny jest pod adresem http://jakarta.apache.org/log4j. 15.3.1. Kod klasy Log4jClient.java 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 package systemy.client; import org.apache.log4j.Logger; import org.apache.log4j.BasicConfigurator; public class Log4jClient { static Logger log = Logger.getLogger(Log4jClient.class); public static void main(String[] args) { BasicConfigurator.configure(); log.info("J2EE Project"); // kod klienta... } } log.info("Koniec"); 15.3.2. Plik właściwości biblioteki log4j – log4j.properties 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 log4j.rootCategory=DEBUG, CONSOLE, FILE log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender log4j.appender.CONSOLE.Threshold=INFO log4j.appender.CONSOLE.Target=System.out log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout log4j.appender.CONSOLE.layout.ConversionPattern=%d{ABSOLUTE} %-5p [%c{1}] %m%n log4j.appender.FILE=org.apache.log4j.RollingFileAppender log4j.appender.FILE.threshold=DEBUG log4j.appender.FILE.layout=org.apache.log4j.PatternLayout log4j.appender.FILE.layout.ConversionPattern=%d{ABSOLUTE} %-5p [%c{1}] %m%n log4j.appender.FILE.append=true log4j.appender.FILE.file=client.log log4j.appender.FILE.bufferedIO=false log4j.appender.FILE.maxBackupIndex=3 log4j.appender.FILE.maxFileSize=10kB http://j2ee.ie.tu.koszalin.pl -78- XVI.ANT i MAVEN 16.1. Ant W celu automatyzacji przygotowania aplikacji EAR bardzo często wykorzystuje się narzędzie ANT. Projekt ten we wczesnym stadium rozwoju był przeznaczony do wspomagania budowy serwera Tomcat. Oba projekty należą do fundacji Apache i są w pełni darmowe. Ideą narzędzia jest tworzenie skryptów w języku XML, dzięki temu uzyskuje się niezależność od systemu opercyjnego. Nasza aplikacja nie potrzebuje teraz osobnych skryptów instalacyjnych np. dla windows i(*.bat) linux'a (*.sh). Jedynym warunkiem jest oczywiście instalacja javy. Aktualnie projekt jest na tyle zaawansowany, że jest wykorzystywany przez wszystkie znaczące narzędzia projektowe javy. Architektura ANT'a wspiera tworzenie wtyczek (plug-in), które znacznie rozszerzają funkcjonowanie narzędzia. 16.1.1. Skrypt ANT'a z J2EE Project – build.xml 1380 <?xml version="1.0" encoding="UTF-8"?> 1381 <project name="J2EE Project ANT Script" default="deploy" basedir="."> 1382 1383 <!-- <splash/> --> 1384 1385 <!-1386 UWAGA! Plik build.xml spełnia następujące założenia projektowe 1387 1388 1. Źródła wszystkich klas javy znajdują się w podkatalogu src. 1389 2. Zmienna app.name określa nazwe aplikacji EAR. 1390 - archiwym EAR test.ear <- nazwa aplikacji dla app.name="test" 1391 1392 Zmiana zmiennej app.name wymusza aktualizację jej nazwy w deskryptorze application.xml 1393 1394 3. Skrypt uwzględnia tworzenie pojedynczego archiwum ejb-jar, war 1395 o następujących nazwach: 1396 1397 - archiwum WAR war-ic.war 1398 - archiwum EJB ejb-jar.jar 1399 1400 code by ([email protected]) ver. 20.12.2004 1401 --> 1402 1403 <!-- =================================================================== --> 1404 <!-- Init --> 1405 <!-- =================================================================== --> 1406 <target name="init"> 1407 1408 <!-- rejestracja log'ów 1409 <record name="verbose.log" action="start" loglevel="verbose"/> 1410 <record name="verbose.log" loglevel="debug"/> 1411 --> 1412 1413 <!-- =============================================================== --> 1414 <!-- parametry do modyfikacji znajdują się w pliku build.properties --> 1415 <!-- =============================================================== --> 1416 <property environment="env" /> 1417 <property name="j2ee.home" value="${env.J2EE_HOME}" /> 1418 <property file="build.properties" /> 1419 <property name="src.dir" value="${basedir}/src" /> 1420 1421 <property name="build.dir" value="${basedir}/build" /> 1422 <available property="build.dir.exists" file="${build.dir}"/> 1423 1424 <!-- lokalizacja tymczasowa plików dla aplikacji EJB JAR --> http://j2ee.ie.tu.koszalin.pl -79- 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 <property name="build.ejb.dir" value="${build.dir}/ejb" /> <!-- lokalizacja tymczasowa plików dla aplikacji WAR --> <property name="build.war.dir" value="${build.dir}/war" /> <!-- lokalizacja tymczasowa plików dla aplikacji EAR --> <property name="build.ear.dir" value="${build.dir}/ear" /> <property name="j2ee.server.name" value="localhost" /> <property name="j2ee.classpath" value="${j2ee.home}/lib/j2ee.jar:${j2ee.home}/ lib/system/cloudscape.jar:${j2ee.home}/lib/system/tools.jar:${j2ee.home}/ lib/cloudscape/RmiJdbc.jar:${j2ee.home}/lib/cloudscape/cloudclient.jar:${j2ee.home}/ lib/classes:${j2ee.home}/classes:${j2ee.home}/lib/locale:${j2ee.home}/lib/jhall.jar:$ {junit.lib}:${cactus.lib}:" /> </target> <!-- =================================================================== --> <!-- Prepare unless="build.dir.exists" --> <!-- =================================================================== --> <target name="prepare" depends="init" > <mkdir dir="${build.dir}"/> <mkdir dir="${build.ejb.dir}"/> <mkdir dir="${build.war.dir}"/> <mkdir dir="${build.ear.dir}"/> </target> <!-- =================================================================== --> <!-- Compile --> <!-- =================================================================== --> <target name="compile" depends="prepare"> <!-- kompilacja klas dla archiwum EJB --> <javac srcdir="${src.dir}" destdir="${build.ejb.dir}" classpath="${j2ee.classpath}" debug="on"> <!-- w tym miejscu mozna określić dokładnie jakie klasy projektu mają należeć do EJB JAR --> <include name="**"/> <!-- wszystkie --> <exclude name="**/client/**"/> <!-- z wyjątkiem należących do pakietu client --> <exclude name="**/war/**"/> <!-- z wyjątkiem należących do pakietu war --> </javac> <!-- kompilacja klas dla archiwum WAR --> <javac srcdir="${src.dir}" destdir="${build.war.dir}" classpath="${j2ee.classpath}"> <include name="**/war/**"/> <!-- wszystkie należące do pakietu war --> </javac> </target> <!-- =================================================================== --> <!-- Jar --> <!-- =================================================================== --> <target name="jar" depends="compile"> <!-<!-<!-<jar ==================================================== --> pakowanie archiwum EJB JAR --> ==================================================== --> jarfile="${build.ear.dir}/ejb-jar-ic.jar"> <metainf file="${basedir}/ejb-jar/META-INF/ejb-jar.xml"/> <fileset dir="${build.ejb.dir}" /> </jar> <!-<!-<!-<war ==================================================== --> pakowanie archiwum WAR --> ==================================================== --> warfile="${build.ear.dir}/war-ic.war" webxml="${basedir}/war/WEB-INF/web.xml"> <fileset dir="${basedir}/war"/> <classes dir="${build.war.dir}"/> <!-- <lib dir="${jars}"><include name="test.jar"/> </lib> --> </war> <!-<!-<!-<ear ==================================================== --> pakowanie archiwum EAR = EJB JAR + WAR + deskryptory --> ==================================================== --> earfile="${build.dir}/${app.name}.ear" appxml="${basedir}/ear/META-INF/application.xml"> <metainf file="${basedir}/ear/META-INF/sun-j2ee-ri.xml/"/> <fileset dir="${build.ear.dir}" includes="*.jar,*.war"/> </ear> </target> <!-- =================================================================== --> <!-- Deploy --> <!-- =================================================================== --> <target name="deploy" depends="jar"> http://j2ee.ie.tu.koszalin.pl -80- 1507 <java classname="com.sun.enterprise.tools.deployment.main.Main" fork="yes"> 1508 <classpath path="${j2ee.classpath}" /> 1509 <sysproperty key="com.sun.enterprise.home" value="${j2ee.home}" /> 1510 <sysproperty key="org.omg.CORBA.ORBInitialPort" value="1050" /> 1511 <sysproperty key="java.security.policy" 1512 value="${j2ee.home}/lib/security/java.policy" /> 1513 <arg line="-deploy ${build.dir}/${app.name}.ear ${j2ee.server.name}" /> 1514 </java> 1515 1516 <waitfor maxwait="10000"> 1517 <http url="http://localhost:8000/project/TestServlet"/> 1518 </waitfor> 1519 1520 </target> 1521 1522 <!-- =================================================================== --> 1523 <!-- Undeploy --> 1524 <!-- =================================================================== --> 1525 <target name="undeploy" depends="init"> 1526 1527 <java classname="com.sun.enterprise.tools.deployment.main.Main" fork="yes"> 1528 <classpath path="${j2ee.classpath}" /> 1529 <sysproperty key="com.sun.enterprise.home" value="${j2ee.home}" /> 1530 <sysproperty key="org.omg.CORBA.ORBInitialPort" value="1050" /> 1531 <sysproperty key="java.security.policy" 1532 value="${j2ee.home}/lib/security/java.policy" /> 1533 <arg line="-uninstall ${app.name} ${j2ee.server.name}" /> 1534 </java> 1535 1536 </target> 1537 1538 <!-- =================================================================== --> 1539 <!-- Clean --> 1540 <!-- =================================================================== --> 1541 <target name="clean" depends="init"> 1542 <delete dir="${build.dir}" /> 1543 </target> 1544 1545 <!-- =================================================================== --> 1546 <!-- JacaDoc --> 1547 <!-- =================================================================== --> 1548 <target name="javadoc"> 1549 <javadoc packagenames = "*" sourcepath="${src.dir}" destdir="doc" 1550 classpath="${j2ee.classpath}" /> 1551 </target> 1552 1553 <!-- =================================================================== --> 1554 <!-- SignJar --> 1555 <!-- =================================================================== --> 1556 <target name="signJar" depends="jar"> 1557 <!-- podpisz archiwum EAR certyfikatem --> 1558 <signjar keystore="${basedir}/security/certs" 1559 jar="${build.dir}/${app.name}.ear" alias="portal" 1560 storepass="portal" keypass="portal"/> 1561 </target> 1562 <!-- =================================================================== --> 1563 1564 </project> 16.2. Maven Znacznym postępem w narzędziach do budowy i integrowania aplikacji jest projekt MAVEN. Można go przyrównać do Anta tak jak postęp języka C++ względem C). Maven potrafi odnaleźć w sieci brakującą bibliotekę, integrować zasoby z różnych serwerów CVS. http://j2ee.ie.tu.koszalin.pl -81- XVII.JBoss i XDoclet 17.1. JBoss Ten rozdział będzie posiadał opis wszystkich wcześniejszych etapów projektu z wykorzystaniem serwera JBOSS dostepnego pod adresem www.jboss.org. Jest to darmowy projekt OPEN SOURCE wykorzystujący kontener webowy w postaci serwera Tomcat lub Jetty. Info! W sieci istnieje polskie forum dotyczące serwera JBOSS – www.jboss.pl prowadzone przez ludzi związanych z firmą JAVART. 17.2. XDoclet Aby zminimalizować ilość pisanego kodu szerokie zastosowanie ma biblioteka xdoclet, która analizując źródła javy wyszukuje teksty zawarte w komentarzach poprzedzone znaczkiem @ (np. @jboss, @ejb). Na podstawie atrybutów, bo o nich mowa, można wygenerować dodtkowe pliki (np. deskryptory xml, czy też źródła klas lub interfejsów java). http://j2ee.ie.tu.koszalin.pl -82- XVIII.Geronimo Darmowy serwer JBoss jes rozwijany przez komercyjną firmę. Czerpie ona zyski głównie ze wsparcia i sprzedaży dokumentacji do serwera. Fundacja Apache znana z serwera Tomcat rozpoczęła rozwó własnego darmowego projektu Geronimo. Aktualnie jest to wersja rozwojowa Geronimo-1.0M3 i nie implementuje jeszcze wszystkich mechanizmów zgodnych ze specyfikacją J2EE. W przyszłości zakłada się większe wsparcie niniejszej dokumentacji o opis serwera Geronimo. http://j2ee.ie.tu.koszalin.pl -83- XIX. Atrapa EJB ? MockEJB Projektowanie komponentów z ciągłym ich testowaniem na rzeczywistym serwerze jest zadaniem czasochłonnym. Aby zminimalizować ten proces można zastosować atrapę kontenera EJB i cały projektowany kod wykonywać znacznie szybciej bez całego procesu tworzenia infrastuktury aplikacji enterprise (wszelkich deskryptorów rozmieszczenia, czy też generacji szkieletów klas wykorzystywanych przy połączeniach zdalnych) wraz z jej instalacją. Pod adresem http://www.mockejb.org znajduje się zestaw bibliotek zawierających wszelkie niezbedne klasy z pakietu javax.ejb jak równierz inne związane z platformą J2EE. 19.1. Aplikcja klienta 19.1.1. Zmodyfikawana klasa klienta - SimpleMockClient.java 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 package systemy.client; import java.util.Properties; import javax.naming.Context; import javax.naming.InitialContext; import javax.rmi.PortableRemoteObject; import org.mockejb.MockContainer; import org.mockejb.SessionBeanDescriptor; import org.mockejb.jndi.MockContextFactory; import systemy.session.Facade; import systemy.session.FacadeBean; import systemy.session.FacadeHome; public class SimpleMockClient { public static void main(String[] args) { //Properties env = new Properties(); //J2EE RI 1.3.1 //env.put("java.naming.factory.initial","com.sun.enterprise.naming.SerialInitContextFactory"); //env.put("java.naming.provider.url", "iiop://localhost:1050"); //Context ctx = new InitialContext(env); try { //============================================================================= MockContextFactory.setAsInitial(); Context ctx = new InitialContext(); //============================================================================= MockContainer mockContainer = new MockContainer(ctx); SessionBeanDescriptor descriptor = new SessionBeanDescriptor("ejb/FacadeBean",FacadeHome.class, Facade.class, new FacadeBean () ); mockContainer.deploy(descriptor); //============================================================================= //reszta kodu bez zmian Object objref = ctx.lookup("ejb/FacadeBean"); //nazwa JNDI FacadeHome home = (FacadeHome) PortableRemoteObject.narrow(objref,FacadeHome.class); Facade w = home.create(); System.out.println(w.test()); //test http://j2ee.ie.tu.koszalin.pl -84- 1609 1610 } catch (Exception e) { 1611 System.err.println(e); 1612 } 1613 } 1614 } Testowanie projektu polega na rezygnacji ze standardowej biblioteki j2ee.jar i w zamian zastosowanie bibliotek *.jar zawartych w katalogu lib projektu MockEJB. Wykonanie kodu klienta powoduje wygenerowanie na konsoli nastepujących komunikatów: [INFO] FacadeBean: setEntityContext(SessionContext org.mockejb.MockEjbContext@91cee) [INFO] FacadeUserBean: ejbCreate() [INFO] FacadeBean: test() test Podczas testów nie tworzono żadnych deskryptorów. Cołość została zahermetyzowana obiektem MockContainer, który kontroluje wywoływanie metod komponentu FacadeBean – w tym przypadku metody test() wyświetlającej komunikat test. Aktualnie jest dostępna wersja mockejb-0.6-beta2.zip zawierajaca kilka przykładów odnoszących się do różnych komponetów ejb oraz transakcji. Dodatkowo w przykładach wykorzystano mechanizm testów jednostkowych projektów JUnit oraz Cactus. Info! Ciekawy artykuł na temat wykorzystywania bibliotek atrap (mock) znajduje się pod adresem http://jdn.pl/do/news/view?id=67 http://j2ee.ie.tu.koszalin.pl -85- XX.Systemy kontroli wersji Cvs i Svn 20.1. CVS Opis w przygotowaniu... 20.2. SVN Opis w przygotowaniu... svn --version svn, version 1.1.0 (r11180) compiled Oct 4 2004, 21:11:18 Copyright (C) 2000-2004 CollabNet. Subversion is open source software, see http://subversion.tigris.org/ This product includes software developed by CollabNet (http://www.Collab.Net/). The following repository access (RA) modules are available: * ra_dav : Module for accessing a repository via WebDAV (DeltaV) protocol. - handles 'http' schema - handles 'https' schema * ra_local : Module for accessing a repository on local disk. - handles 'file' schema * ra_svn : Module for accessing a repository using the svn network protocol. handles 'svn' schema svn --help usage: svn <subcommand> [options] [args] Type "svn help <subcommand>" for help on a specific subcommand. Most subcommands take file and/or directory arguments, recursing on the directories. If no arguments are supplied to such a command, it will recurse on the current directory (inclusive) by default. Available subcommands: add blame (praise, annotate, ann) cat checkout (co) cleanup commit (ci) copy (cp) delete (del, remove, rm) diff (di) export help (?, h) import info list (ls) http://j2ee.ie.tu.koszalin.pl -86- log merge mkdir move (mv, rename, ren) propdel (pdel, pd) propedit (pedit, pe) propget (pget, pg) proplist (plist, pl) propset (pset, ps) resolved revert status (stat, st) switch (sw) update (up) Subversion is a tool for version control. For additional information, see http://subversion.tigris.org/ svnadmin --help general usage: svnadmin SUBCOMMAND REPOS_PATH [ARGS & OPTIONS ...] Type "svnadmin help <subcommand>" for help on a specific subcommand. Available subcommands: create deltify dump help (?, h) hotcopy list-dblogs list-unused-dblogs load lstxns recover rmtxns setlog verify svnadmin create d:\svn\reporitory 20.2.1. Tworzenie repozytorium SVN svnadmin create /lokalizacja_repozytorium svn import /katalog_projektu file:///lokalizacja_repozytorium -m "nazwa_projektu" http://j2ee.ie.tu.koszalin.pl -87- XXI.UML 21.1. Diagramy przypadków użycia Opis w przygotowaniu... http://j2ee.ie.tu.koszalin.pl -88- XXII.Projekt Tworząc projekty opieramy się na źródłach mojego projektu będącego swego rodzaju szkieletem do dalszej rozbudowy – http://j2ee.ie.tu.koszalin. Projekt składa się z trzech modułów: • • • komponenty entity beans (utrwalanie danych i relacje CMR); komponenty session bean (wzorce FacadeBean, DTO i ServiceLocator); aplikacja klas klienta (wzorce ServiceLocator i BussnesDelegate, testy JUnit) ID Zagadnienia cząstkowe projektów 2x J2EE Rozproszenie komponentów aplikacji na dwa serwery J2EE SSL Autentykacja w aplikacji EAR (protokół SSL) ROLE Autoryzacja w aplikacji EAR (role użytkowników) CMR Relacje pomiędzy komponentami Entity (1:1, 1:N, M:N) WS Wywoływanie usług sieciowych Web Services (pakiet AXIS) Properties Parametryzacja danych komponentów aplikacji EAR JMS Komunikaty JMS w komponentach EJB MDB LDAP Serwer katalogowy LDAP jUnit Testy jednostkowe jUnit log4j Mechanizm rejestracji log'ów 22.1. Założenia nazewnicze Każda grupa projektowa otrzyma numer projektu, który należy używać przy tworzeniu własnych klas i interfejsów. Głównym celem jest eliminacja sytuacji, gdy kilka projektów nie będzie mogło ze sobą egzystować na jednym serwerze testowym. Np. dla grupy numer 10 wystąpią następujące warunki wstępne: – Nazwa głównego pakietu ma zostać przemianowana z project na project10, a tym samym np. klasa project.entity.Firma będzie teraz klasą project10.entity.Firma. – Wszystkie nazwy JNDI mają zostać przemianowane z dodaniem przedrostka project10 np. dla ejb/Facade będzie teraz ejb/project10/Facade; – Wszystkie projekty mają wykorzystywać do utrwalania danych źródło bazy danych – Cloudscape o nazwie jdbc/Cloudscape; – Nazwy (Abstract Schema Name) stosowane przy komponentach Entity i używane do budowania zapytań w EJB-QL oraz generacji kodu SQL należy zmienić dodając do każdej z nich numer projektu np. dla FirmaBean będzie nazwa FirmaBean10. W wyniku takiej modyfikacji będzie generowana tabel FirmaBean10Table w bazie Cloudscape – w pewien sposób unikatowa dla każdej grupy. http://j2ee.ie.tu.koszalin.pl -89- 22.2. Diagram klas projektu Aplikacja klienta <<Singleton>> <<Singleton>> ServiceLocator ServiceLocator Aplikacja EAR zainstalowana na serwerze J2EE <<EntityEJB>> FirmaBean <<POJO>> Client <<POJO>> <<SessinoEJB>> BusinessDelegate FacadeBean <<EntityEJB>> PracownikBean <<POJO>> DataTransferObject Rys. 52. Diagram podstawowych klas projektu J2EE Project. Nazwy zawarte w znakach << .... >> oznaczają stereotypy. Wskazuje to na rodzaj implementacji danej klasy w rzeczywistym systemie. • <<POJO>> - (ang. Plain Old Java Object) oznacza zwykłe obiekty JAVA; • <<Singleton>> - istnienit tylko jednej instancji obiektu z danej klasy; • <<SessionEJB>> - obiekt Session Bean wymagający istnienia kontenera EJB; • <<EntityBEan>> - obiekt EntityBean wymagający istnienia kontenera EJB. http://j2ee.ie.tu.koszalin.pl -90- 22.3. Lista tematów Lp Opis Technologia Rozszerzyć projekt o obsługę Konta, na które będą przekazywane środki pieniężne – aktywacja dokonująca transferu gotówki ma myć dokonywana przy użyciu klientów JMS i 2 komponentów MDB. Każdy z komponentów ma 1 obsługiwać inny typ kolejki JMS. JMS, MDB Rozszerzyć projekt o obsługę Konta, na które będą przekazywane środki pieniężne – aktywacja dokonująca transfer gotówki ma być dokonywana przy 2 użyciu technologii WebServices (AXIS) WebService, AXIS Zasymulować komunikację dwóch serwerów J2EE na jednym komputerze i dokonać rozproszenia części komponentów Entity Bean używając interfejsów zdalnych. Dodać dodatkowe metody biznesowe rozszerzające funkcjonalność 3 systemu o możliwość zatrudniania jednego pracownika w dwóch firmach 2xJ2EE, CMR Rozszerzyć system o komponent biznesowy ResourceBean przechowujący informacje o zasobach udostępnianych pracownikom firmy. Przez zasoby rozumiem środki trwałe jak np. komputer, samochód, telefon komórkowy. Kod aplikacji klienta ResourceClient.java ma umożliwiać wyświetlanie informacji o danym użytkowniku i zasobach jakie aktualnie posiada. W projekcie wykorzystać pakiet jUnit do realizacji testów jednostkowych klasy 4 ResourceClientTest.java CMR, jUnit Zrealizować rejestrację użytkowników firmy przez zewnętrzny system JNDI, wysyłania komunikatów JMS. Podczas rejestracji nowego użytkownika jego dane JMS 5 mają zostać także umieszczone na serwerze LDAP. Zrealizować aplikację klienta (np. działu wysyłania reklam do firm) aby pełniła rolę odbiorcy informacji o komunikatach JMS przesyłanych przez system w przypadku rejestracji nowej firmy. Rejestrację nowej firmy zrealizować jako prostą aplikację war z formularzem dodawania danych firmy. Wykorzystać 6 wzorzec delegata biznesowego hermetyzującego kod obsługi JMS klienta. LDAP, JMS, BussDelegate Rozszerzyć system rejestrujący pracowników o nowe pola. Dodać zestaw funkcji EJB-QL wyszukujących według różnych kryteriów, których deklaracje należy umieścić w interfejscie PracownikLocalHome. Kod definicji funkcji będzie generowany na podstawie języka zapytań EJB-QL. Opracować różne warianty, aby 7 zaprezentować możliwości składniowe EJB-QL. Rozszerzyć system o komponent MagazynBean przechowujący towary danej firmy. Zamodelować mechanizm przechowywania informacji o sprzedaży danego towaru przez konkretnego pracownika. Zaprojektować metodę biznesową obliczającą prowizję (np. 1,5%) od wartości towarów sprzedanych w 8 danym miesiącu. Facade, CMR Rozszerzyć klasę ServiceLocator o możliwość wyszukiwania usług JMS. Zaprezentować jej działanie od strony klienta oraz od strony komponentów EJB. Jako problem do rozwiązania przy użyciu tej technologii przyjąć system, który przesyła informację o nakazie wykonania badań okresowych zatrudnionych w danej firmie. Do odbioru danych wykorzystać prosty komponent MDB 9 przekazujący informacje na konsolę serwera. ServiceLocator, JMS, MDB http://j2ee.ie.tu.koszalin.pl -91- Lp Opis Technologia Rozszerzyć komponent PracownikBean o możliwość definiowania okresu urlopowego. Do tego celu wykorzystać wzorzec transferu danych do stworzenia klasy PracownikUrlopData.java. Zrealizować aplikację klienta, który będzie pobierał listę urlopów pracowniczych posortowaną według rozpoczęcia lub 10 długości trwania. DTO, Facade,BussDe legate Rozbudować aktualną aplikację o testy jednostkowe realizowany przy pomocy projektu Cactus (http://jakarta.apache.org/cactus). Biblioteka ta to sposób na osadzanie biblioteki jUnit na serwerze i jej zdalne wywoływanie przez aplikację webową. Zorganizować serię testów na wszystkich klasach, a w szczególności 11 komponentach EntityBean. Junit, Cactus Klient SOAP wykonuje zlecenie przelewu gotówki pomiędzy kontem Firmy i Pracownika. Zaadaptuj system do obsługi takiej operacji na komponentach FirmaBean i PracownikBean (każdy z komponentów rozszerzyć o pole double 12 konto) SOAP, CMP Rozszerzyć aplikację o obsługę szyfrowania komunikacji klienta RMI z SSL, ANT, wykorzystaniem szyfrowania SSL. Opracować i opisać mechanizm szyfrowania i Certyfikaty generacji certyfikatów dla aplikacji J2EE. Wykorzystać ANT'a do 13 automatycznego podpisywania archiwów EAR. Rozbudować system o mechanizm wersjonowania Firm i Pracowników. Dane z systemu nie mają znikać, a jedynie się dopisywać. Wymaga to dodania dodatkowego identyfikatora do klas EntityBean zawierającego idFirmy i idPracownika, daty zmiany, oraz identyfikatora ważności danego wpisu – czy jest bieżący, czy też już historyczny. Rozszerzyć metody wyszukiwania po 14 określonym idFirmy i idPracownika. CMR Zamodelować usługę Web Services udostępniającą możliwość realizacji Web Services raportów na temat historii zmian wprowadzanych w systemie. W tym celu każda metoda biznesowa opisana w komponencie fasady musi być wzbogacona o tworzenie obiektu Entity Bean o nazwie HistoriaBean w którym należy dokonywać zapisu czasu, wykonanej operacji i identyfikatorach id (kluczach 15 głównych) Pracownika i/lub Firmy Rozszerz system o implementację forum na komponentach entity ForumBean i Kategoria Bean. Zrealizuj mechanizm dodawania nowych wiadomości przez autoryzowanych użytkowników. Komponent Kategoria ma umożliwiać 16 realizacje relacji cyklicznych (kategoria może zawierać podkategorie) Role, Autoryzacja CMR Opracować rozbudowaną aplikację kliencką umożliwiającą tylko odczyt danych o pracownikach i firmach. W tym celu zastosować określone ROLE dostępu do metod. Ograniczyć dostęp do metod setXXX). W projekcie należy uwzględnić wszelkie możliwości parametryzowania projektu (zmiana serwera JNDI, 17 Properties itd...) Autoryzacja, FacadeBean, Propertes Zaprojektować komponent sesyjny JndiBean za pomocą którego klient przez SessionBean, pośrednictwo fasady będzie miał dostęp do wszystkich nazw JNDI JNDI zdefiniowanych na serwerze J2EE. Zaimplementować operacje przeglądania (list), wyszukiwanie (lookup) oraz dodawania własnych nazw. Dodawanie nazw 18 wykonać z użyciem autentykacji – tylko dla określonej roli np. admin Rozszerzyć projekt o testowanie Delegata Biznesowego klienta przy pomocy 19 biblioteki jUnit. Zaprezentować zestaw testów wraz z ich pogrupowaniem. http://j2ee.ie.tu.koszalin.pl JUnit, BussDelegate -92- Lp Opis Technologia Rozszerzyć projekt o testowanie Fasady Sesji umieszczonej na serwerze. W tym celu rozbudować fasadę o nowe metody i do testów użyć biblioteki JUnitEE. Zaprezentować testy wykorzystujące komunikację komponentów EJB z 20 wykorzystaniem interfejsów zdalnych i lokalnych. JUnitEE, FasadeBean Firmy wykorzystujące nasz system zostały w nim zarejestrowane. Dodatkowo każda z firm mogła się zarejestrować we własnej kolejce komunikatów (Queue) naszego serwera. Zrealizuj wysyłanie do kolejki danej firmy komunikatu podczas wstawiania i usuwania jej pracownika. Jako treść komunikatu należy przesyłać nazwisko, imię i PESEL, ale bez innych szczegółowych informacji. Wysyłki należy dokonać tylko, gdy dana firma używa kolejki. (rozszerzyć komponent FirmaBean o pole String zawierające null -brak kolejki lub jej nazwę 21 np. jms/QueueFirmaA itd... ) JMS, EntityBean, JNDI Opracować komponent sesyjny stanowy LicznikiBean realizujący rejestrację liczników “biznesowych” danego klienta. Licznik może być wykorzystany do zliczania np. liczby wywołać metody przelew, liczby nieudanych wywołać metody wypłata itd... Komponent powinien posiadań obiekt Map, którego zadaniem będzie przechowywanie par wartości (klucz=wartość). Zaimplementować wyszukiwaniem według klucza i zwiększanie jego wartości (inkrementacja). Uwaga! Zadeklarować możliwość wywoływanie metod 22 komponentu w transakcji jak i bez nich. FacadeBean, transakcje Zamodelować komponenty TowarBean(Entity) oraz ZakupyBean (Session). Komponent zakupów powinien posiadać możliwość podliczania ceny z uwzględnieniem stóp podatkowych za dany towar (A=22%, B=7%, C=..., ...). Wartości stóp procentowych należy deklarować z poziomu konfiguracji 23 Deploytool (nie w kodzie java). EntityBean, SessionBean, Parameters System raportowania pod wpływem otrzymania komunikatu JMS z kolejki z JMS, MDB, informacją o danej firmie (np. jej REGON) oraz treści komunikatu (np. tekst JavaMail pozdrowień świątecznych) ma wysłać otrzymaną treść wszystkim pracownikom danej firmy zarejestrowanym w systemie. Do wysyłki użyć JavaMAIL i 24 komponentu MDB. Zaprojektowany system ma być pośrednikiem pomiędzy systemami magazynowymi naszych klientów – Firm. Zrealizuj mechanizm odpytywania wybranych firm (np. z danego regiony, lub o danym profilu – rozszerzyć komponent FirmaBean o dodatkowe właściwości). Klient Java komunikuje się z komponentem FacadeBean, który pobiera odpowiednią listę firm. Następnie wykonuje do każdej z nich odwołanie protokołem SOAP o cenę danego towaru (np. książka o danym numerze ISBN). Podczas kolejnych odwołań następuje zapamiętanie najkorzystniejszej oferty. Po wykonaniu operacji informacja zwrotna trafia do klienta (klienta można obsługiwać także przez SOAP, RMI lub 25 JMS). SOAP, Web Services, Session Bean Rozbudować system o komponent ZusBean przechowujący kwotę opłaty przekazanej przez firmę za zatrudnionych pracowników. W komponencie FacadeBean zrealizować metody dodawania nowego wpisu (np. za dany miesiąc), określania kwoty na podstawie sumy kwot każdego pracownika danej firmy. Komponent PracownikBean musi posiadać pole kwotaZus oraz 26 odpowiednie metody do jego modyfikacji. EntityBean, FacadeBean Na serwerze LDAP przechowano informacje o firmach. Zaprojektować metodę LDAP, JMS aktywowaną przy pomocy komunikatu JMS, której zadaniem jest przeglądniecie drzewa LDAP i odnalezienie firm (DirContext), które nie zostały jeszcze 27 zarejestrowane w FirmaBean. Dokonać automatycznej rejestracji firm. http://j2ee.ie.tu.koszalin.pl -93- Lp Opis Technologia System przechowuje informacje o pracownikach. Rozszerz jego funkcjonalność o EntityBean, możliwość przechowywania informacji na temat okresów pracy. W tym celu FacadeBean zadeklaruj komponent GrafikBean, który będzie przechowywał informację jaki pracownik, dla jakiej firmy (nie musi do niej należeć), przez jaki okres (ile godzin i od której godziny) i w jakim dniu wykonywał swoją pracę. Zadeklaruj metody wyświetlania informacji dla danego pracownika i np. grafik za dany 28 tydzień/miesiąc dla wszystkich pracowników. Należy przemodelować system w ten sposób,że nazwa komponent EntityBean, PracownikBean będzie teraz określała komponent PrzesyłkaBean. W ten sposób EJB-QL system może pełnić rolę rejestracji firm oraz rejestracji przesyłek kurierskich, które mają trafić pod podany adres. Każda przesyłka ma posiadać pole “status”. Zaimplementować możliwość wycofania przesyłki (status=WYCOFANIE), doręczenia (status=DORECZONO) i oczywiście tryb przesyłania (status=NADAWCA, status=W_DRODZE, status=ODBIORCA). Umożliwić wyświetlanie informacji o przesyłkach danej 29 firmy według ich statusu. Zaprojektować testowanie logiczne aplikacji J2EE Project z wykorzystaniem atrapy testów MockEJB. Całość opracować z wykorzystaniem biblioteki testów jednostkowych JUnit. Dodatkowo w klasach adaptacyjnych wykorzystać 30 bibliotekę Log4J do rejestracji logów. MockEJB, JUnit, Log4J Rozszerzyć aplikację o walidację danych. Zastosować dodatkowe metody w klasach DTO – FirmaData i PracownikData. Określić pola komponentów EntityBean, które muszą być wypełnione oraz pola dodatkowe. Rozszerzyć walidację o mechanizm analizy takich pól jak NIP, REGON i PESEL (liczba cyfr i 31 sumy kontrolne). Walidacji dokonywać na serwerze w warstwie Fasady Session. DTO, Facade Session, Validate Zrealizować komponent RssBean jako komponent BMP. Zamodelować mechanizm pobierania i wstawiania danych z tabeli RssTable bazy danych Cloudscape. Informacje uzyskane z połączenia cache'ować w lokalnej liście w FacadeBean w postaci obiektów klasy RssData (według wzorca DTO). Ma to na celu eliminacje zbyt częstych odwołać do bazy np. tylko raz na jeden dzień. Klient ma mieć możliwość pobierania listy obiektów RssData oraz dokumentu 32 RSS (XML). BMP, DTO, RSS Rozszerz aplikację EAR o nową aplikację enterprise JAR zawierającą kopię EntityBean, komponentów FirmaBean i PracownikBean. Zadaniem jest podział naszej EJB-JAR, aplikacji na część produkcyjną i nową testową, która jest jeszcze w rozwoju – np. FacadeBean będzie miała dodatkową funkcjonalność. Komponent FacadeBean ma obsługiwać wywoływania obu kopii komponentów przy czym błędy wynikłe w warstwie testowej nie mają wpływać na pracę warstwy produkcyjnej – informowanie o błędach mają być przesyłane np. na konsolę serwera. Uwaga! Kopie komponentów Entity mają mieć inne nazwy JNDI np. 33 ejb/test/Pracownik. Wyeksportuj wnętrze metod biznesowych znajdujących się w komponencie FacadeBean do podległych klas wzorca Application Service. Celem jest ograniczenie logiki biznesowej w fasadzie przez dodanie dodatkowej warstwy biznesowej. W tym celu zamodeluj klasy FirmaApplicationService oraz PracownikApplicationService. W każdej z nich umieść logikę do danego 34 przypadku użycia: pracownika i firmy. http://j2ee.ie.tu.koszalin.pl FacadeBean, Application Service -94- Lp Opis W zaprojektowanym systemie zamodelować utrwalanie danych komponentów EntityBean dla innych typów danych (w przykładach wykorzystano tylko String). W tym celu rozszerzyć komponenty PracownikBean i FirmaBean o dodatkowe pola np. daty rejestracji, daty zatrudnienia, liczby dni urlopu. Dla nowo przygotowanych komponentów stworzyć rozszerzone wersje obiektów 35 transferu danych DTO – FirmaExtData i PracownikExtData. Technologia Entity Bean, DTO Zamodelować komponenty FirmaBean i PracownikBean jako komponenty BMP. BMP, relacje Do utrwalania danych wykorzystać własny mechanizm oparty o JDBC. Można również wykorzystać projekt Hibernate lub JDO automatyzujący proces 36 generacji klas do komunikacj z JDBC. Zamodelować generację wartości kluczy głównych (String) jako klasę wywoływaną z lokalizatora usług o nazwie Key.java z polem inkrementowanym. Wówczas w klasach FirmaBean i PracownikBean wykorzystać pobieranie klucza zamiast jego generacji przy użyciu klasy Guid.java. Uwaga! Zastosować standardowy mechanizm synchronizacji przy pobieraniu wartości klucza, aby wyeliminować możliwość pobrania tej samej 37 wartości przy jednoczesnym odwołaniu do klasy generującej. ServiceLocator, EntityBean Zaprojektuj komponent sesyjny InfoJndiBean , który aplikacji klienta JNDI, (delegatowi) będzie zwracał informację o liście nazw JNDI komponentów EJB. W Facadebean, tym celu zaprojektuj dodatkową klasę JNDIName.java zawierającą publiczne BussDelegate pola statyczne np. public String FIRMA_NAME = “ejb/Firma”. Wówczas w aplikacji wszelkie odwołania “ejb/Firma” mają zostać zamienione na odwołania do JNDIName.FIRMA_NAME. Podobnych operacji dokonać dla wszystkich nazw JNDI wykorzystywanych w referencjach fasady. Wszelkie nazwy muszą być zarejestrowane w liście komponentu InfoJndiBean z możliwością ich 38 udostępniania klientowi. Opracuj klasy PracownikPK i FirmaPK i użyj ich do tworzenia obiektów kluczy PK, głównych komponentów Entity CMP. Wówczas podczas mapowania pół id dla CMP Firmabean i PracownikBean należy jako klucz wybrać nasze klasy: project.pk.PracownikPK i project.pk.FirmaPK. W klasach komponentów metody ejbCreate(...) będą teraz zwracały zamiast String obiekty naszej klasy. Również należy wnieść poprawki przy deklaracji metod create() lokalnych interfejsów 39 domowych. Rozbudować projekt o realizację Web Services. W tym celu rozbudować projekt o 2xJ2EE, możliwość komunikacji dwóch aplikacji J2EE. W przypadku wstawiania nowej Web Services firmy w jednym systemie (ServerA → EntityEJB) automatycznie ma zostać wykonana usługa wstawiania takiej samej firmy w drugim systemie 40 (ServerA→ Web Services → SererB → EnityEjb) http://j2ee.ie.tu.koszalin.pl -95- Przykładowe zagadnienia rozszerzające standardowy projekt: • • • • • • • • • • • serwer LDAP dwa serwery J2EE ze wspólnym serwerem JNDI rejestracja komponentów w zewnętrznym systemie LDAP plik XML opisujący przelew klienta banku (Web Services) przesyłanie komunikatów JMS pomiędzy dwoma serwerami J2EE wywoływanie procedur RPC w WebService z poziomu EJB komunikacja asynchroniczna JMS dwóch systemów przelew bankowy – obsługa transakcyjności deklaratywnej przelew bankowy – obsługa transakcyjności programowej testy Jednostkowe jUnit oraz Cactus bezpieczeństwo – autentykacja i autoryzacja użytkowników komponentów EJB http://j2ee.ie.tu.koszalin.pl -96- 22.4. Zagadnienia podstawowe • • • • • • • • • • • Komunikacja klienta z komponentem EJB za pośrednictwem JNDI; Budowa komponentów EJB (interfejsy i klasa); Cel i sposób realizacji wzorca Fasady; Różnice pomiędzy komponentami sesyjnymi EJB stateless i stateful oraz przykłady zastosowań; Mechanizm utrwalania danych stosowany w komponentach EJB; Mechanizm deklarowania referencji komponentów EJB w innych komponentach EJB; Cel i sposób realizacji wzorca obiektu transferu danych DTO; Różnice w definiowaniu interfejsów zdalnych i lokalnych; Omów cel stosowania abstrakcyjnych klas EntityBean dla CMP 2.X; Omów mechanizm definiowania relacji CMR komponentów entity bean; Co rozumiesz przez pojęcie transakcji? Opisz zasady ACID; Do projektu zostanie udostępniony szablon dokumentacji - (w www.OpenOffice.org) http://j2ee.ie.tu.koszalin.pl -97- XXIII.Zasoby WWW Serwery J2EE • J2EE R I 1.3.1 • J2EE RI 1.4 – SunOne • JBoss • Geronimo • Resin • Tomcat Testowanie aplikacji • MockEJB • JUnit • Cactus • JUnitEE Usługi Web Services • AXIS XML • Xerces • Xalan Biblioteki do zarządzania log'ami • Log4j Systemy kontroli wersji • CVS • SVN Platforma ECLIPSE • platforma eclipse – www.eclipse.org Eclipse – plugins Uml • Omondo http://j2ee.ie.tu.koszalin.pl -98- XXIV.Schematy deskryptorów Schemat deskryptora application.xml http://j2ee.ie.tu.koszalin.pl -99- Schemat deskryptora ejb-jar.xml http://j2ee.ie.tu.koszalin.pl -100- http://j2ee.ie.tu.koszalin.pl -101- Schemat deskryptora sun-j2ee-ri.xml http://j2ee.ie.tu.koszalin.pl -102- Schemat deskryptora web-app.xml http://j2ee.ie.tu.koszalin.pl -103- XXV.J2EE RI 1.3.1– poprawki i aktualizacje Standardowa instalacja wzorcowej implementacji J2EE (j2sdkee-1_3_1-win.exe) posiada usterki utrudniające pracę. 25.1. Ścieżka do bazy Cloudscape Uruchomienie serwera j2ee -verbose powoduje wyszukiwanie baz danych znajdujących się w katalogu bieżącym. Aby ustawić stały katalog dla źródeł baz danych zmodyfikowano plik setenv.bat z katalogu bin. Tekst wytłuszczony określa wprowadzone zmiany. 25.1.1. Plik setenv.bat 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 REM REM Copyright 2002 Sun Microsystems, Inc. All rights reserved. REM SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. REM rem rem Set JAVA_HOME and J2EE_HOME before running this script. rem rem first include user-specified definitions. call %J2EE_HOME%\bin\userconfig.bat if "%JMS_HOME%" == "" set JMS_HOME=%J2EE_HOME% set LIBDIR=%J2EE_HOME%\lib set LOCALEDIR=%J2EE_HOME%\lib\locale set CLOUDSCAPE_INSTALL=%LIBDIR%\cloudscape set CLASSESDIR=%LIBDIR%\classes set JMS_CLASSESDIR=%JMS_HOME%\classes set J2EEJARS=%LIBDIR%\j2ee.jar set JAVATOOLS=%JAVA_HOME%\lib\tools.jar set JAVAHELPJARS=%LIBDIR%\jhall.jar rem JMS DB PATH must end in slash to specify a directory set JMS_DB_PATH=%J2EE_HOME%\repository\%COMPUTERNAME%\db\ set SYSTEM_LIB_DIR=%LIBDIR%\system set JMS_RI_JDBC=%SYSTEM_LIB_DIR%\cloudscape.jar;%SYSTEM_LIB_DIR%\tools.jar set CLOUDJARS=%JMS_RI_JDBC%;%CLOUDSCAPE_INSTALL%\RmiJdbc.jar;%CLOUDSCAPE_INSTALL\cloudclient.jar set J2EETOOL_CLASSES=%LIBDIR%\toolclasses set J2EETOOL_JAR=%LIBDIR%\j2eetools.jar set CPATH=%CLOUDJARS%;%CLASSESDIR%;%JMS_CLASSESDIR%;%J2EEJARS%;%J2EETOOL_CLASSES%;% J2EETOOL_JAR%;%LOCALEDIR%;%J2EE_CLASSPATH%;%JAVATOOLS%;%JAVAHELPJARS% 1652 1653 set JAAS_OPTIONS=-Djava.security.auth.policy=%J2EE_HOME%\lib\security\jaas.policy 1654 set SSL_OPTIONS=-Djavax.net.ssl.trustStore=%J2EE_HOME%\lib\security\cacerts.jks 1655 set LISTEN_OPTIONS=Dcom.sun.CORBA.connection.ORBListenSocket=SSL:0,SSL_MUTUALAUTH:0,PERSISTENT_SSL:1060 1656 1657 set CLOUD_OPTIONS=-Dcloudscape.system.home=%J2EE_HOME%\cloudscape 1658 set JAVA_COMMAND=%JAVA_HOME%\bin\java -Xmx128m %CLOUD_OPTIONS% %SSL_OPTIONS% %JAAS_OPTIONS% 1659 http://j2ee.ie.tu.koszalin.pl -104- 25.2. Bibiblioteka klienta CloudView CloudView jest programem (GUI) do obsługi baz Cloudscape. W pakiecie J2EE zawarta jest biblioteca Cloudview.jar, ale nie współpracuje ona z nowszą wersją bazy Cloudscape (biblioteka dla Cloudscape 4.0 a baza Cloudscape 4.06). Należy ze strony www.cloudscape.com ściągnąć odpowiednie poprawki (wraz z tym plikiem dostępne jest archiwum z wszystkimi poprawkami i bibliotekami). Są to pliki cloudscape406.jar oraz plik pomocy jh.jar. Pliki należy skopiować do katalogu lib/cloudscape z nadpisaniem na istniejący plik cloudscape.jar. Dodatkowo w katalogu bin umieszczono plik cloudview.bat wywołujący program CloudView z biblioteki cloudscape.jar. 25.2.1. Plik cloudview.bat 1660 set CLASSPATH=%J2EE_HOME%\lib\system\cloudscape.jar;%J2EE_HOME%\lib\system\cloudutil.jar;% J2EE_HOME%\lib\cloudscape\cloudview.jar;%J2EE_HOME%\lib\cloudscape\jh.jar;%CLASSPATH% 1661 1662 java -ms32M -mx32M COM.cloudscape.tools.cview 1663 1664 pause 25.3. Obsługa serwera w Win98 W przypadku uruchamiania serwera pod systemem Windows 98 mamy do czynienia z większym problemem. W skryptach bat zastosowano rozszerzoną wersję DOS'a i jako parametry poleceń dozwolony jest zapis %*. W tych miejscach w których występuje %* należy wstawić listę parametrów zgodną ze składnią Win98 %0 %1 %2 %3 %4 %5 %6 %7 %8 %9. Dotyczy to wszystkich plików bat. Następny problem jest próba uruchomienia serwera pod Win98 kończąca się wyświetleniem komunikatu - za mało miejsca na środowisko. W takim przypadku najlepiej umieścić na pulpicie skrót do programu command.com /P /E:3000. Po uruchomieniu konsola jest niezależna od wcześniejszych ustawień i ma przydzielony większy obszar roboczy pamięci. Przy uruchamianiu serwera J2EE należy też pamiętać o ustawieniu zmiennych środowiskowych JAVA_HOME i J2EE_HOME na odpowiednie katalogi. 25.4. Gramatyka sun-j2ee-ri_1_3.dtd Podczas tworzenia serwera J2EE RI 1.3.1 firma SUN niedopracowała kilku elementów. Jednym z nich jest źle zdefiniowana gramatyka dla dokumentu sun-j2ee-ri_1_3.dtd. Dodałem brakujące wpisy: 1665 1666 1667 1668 1669 <!ELEMENT <!ELEMENT <!ELEMENT <!ELEMENT <!ELEMENT method (ejb-name, method-intf, method-name, method-params)> method-intf (#PCDATA)> method-name (#PCDATA)> method-params (method-param?)> method-param (#PCDATA)> http://j2ee.ie.tu.koszalin.pl -105- Do znacznika web dodano także możliwość definiowania w jego wnętrzu znacznika module-name. 1670 <!ELEMENT web (display-name, module-name, servlet*, context-root, resource-ref*, ejb-ref*, resource-env-ref*)> Gramatyka jest przydatna podczas edycji dokumentów sun-j2ee-ri.xml, które można automatycznie weryfikować na podstawie podanego schematu. http://j2ee.ie.tu.koszalin.pl -106-