Platforma J2EE i EJB Oprogramowanie systemów równoległych i rozproszonych Wykład 9 Dr inż. Tomasz Olas olas@icis.pcz.pl Instytut Informatyki Teoretycznej i Stosowanej Politechnika Częstochowska Platforma J2EE to zbiór standardów pozwalajacych budować wielowarstwowe, rozproszone aplikacje. Enterprise JavaBeans (EJB) jest jedna z technologii wchodzacych w skład J2EE: zawieraja logikę biznesowa aplikacji, uruchamiane sa w kontenerze EJB - środowisku uruchomieniowym stanowiacym część serwera aplikacji, wykorzystywane sa do budowy złożonych aplikacji rozproszonych na zasadzie składania z klocków, obsługuje zdalnych i lokalnych klientów, klientami moga być aplikacje serwlety, JSP, inne EJB. Rola EJB na platformie J2EE 1/24 Kiedy użyć EJB? W platformie J2EE możemy wyróżnić cztery (pięć) warstw: klienta, sieciowa, logiki biznesowej, zasobów informacyjnych (warstwa pełniaca rolę integracyjna i odpowiadajac a za współpracę z innymi systemami). Nie wszystkie aplikacje J2EE wykorzystuja technologię EJB. EJB realizuja logikę biznesowa, zostawiajac innym elementom aplikacji logikę prezentacji. Aplikacja musi być skalowalna możliwość rozproszenia komponentów Zaawansowane przetwarzanie transakcyjne kontener oferuje usługi transakcyjne deklaratywne specyfikowanie granic transakcji transakcje obiektowe Obsługa różnych typów klientów przegladarka klient aplikacyjny 3/24
Kontener EJB Rodzaje komponentów EJB Komponenty EJB pracuja w ramach kontenera EJB, który jest jednym ze składników serwera aplikacyjnego. Kontener EJB pośredniczy w komunikacji pomiędzy komponentem EJB, a światem zewnętrznym. Kontener EJB oferuje komponentowi szereg usług o charakterze systemowym: ochrona dostępu, obsługa transakcji, itp. Komponent EJB może uzyskać dostęp do usług kontenera EJB korzystajac z mechanizmu typu callback - w chwili powoływania do życia obiektu komponentu EJB, kontener EJB przekazuje komponentowi EJB pewien rodzaj uchwytu zwrotnego - za pomoca tego uchwytu obiekt komponentu EJB może wykonywać wywołania metod kontenera EJB. sesyjny (ang. session) - krótkotrwały obiekt wykorzystywany przez pojedynczego klienta (wykonuje określona operację wywołana przez klienta), nie sa współdzielone (każdy odpowiada wyłacznie jednemu klientowi), nie ma charaktru trwałego, encyjny (ang. entity) - reprezentuje pewna jednostkę danych, trwale zapisana w systemie baz danych (często jest to rekord bazy danych), cechuje się długim czasem życia, ma charakter trwały, modyfikacje bazy danych realizowane sa w sposób atomowy i transakcyjny, współdzielony przez wielu klientów, dostępnych dla wielu sesji. sterowane komunikatami (ang. message-driven) - komunikatowy komponent EJB występuje w postaci obiektu nasłuchujacego w ramach Java Message Service (JMS), uruchamiany wtedy, gdy nadchodzi komunikat od klienta, przetwarzanie komunikatów odbywa się asynchronicznie, może modyfikować zawartość bazy danych, bezstanowy. Komponenty sesyjne 5/24 Komponenty encyjne a sesyjne - różnice Istnieja dwie odmiany komponentów sesyjnych: stanowe komponenty sesyjne (ang. stateful) zmienne instancyjne komponentu reprezentuja stan dla konkretnej sesji z klientem, (stan konwersacji), stan kowersacji obejmuje czały czas trwania sesji - przestaje obowiazywać dopiero w momencie, gdy klient zakończy sesję lub jawnie usunie obiekt. bezstanowe komponenty sesyjne (ang. stateless) nie zapamiętuje stanu konwersacji z klientem, wewnętrzny stan obiektu jest ważny i spójny tylko podczas pojedynczego wywołania, komponenty bezstanowe, jako jedyne, moga implementować usługi internetowe (Web Services). trwałość - stan komponentu encyjnego jest zapisywany w pamięci stałej - czas życia znacznie przekraczajacy okres działania aplikacji. współdzielony dostęp - komponenty encyjne moga być współdzielone przez wielu klientów. Ponieważ każdy z nich może zmienić w tym samym czasie te same wartości ważne jest, aby obiekty encyjne pracowały w ramach transakcji. klucz główny - każdy komponent encyjny posiada unikalny identyfikator zwany kluczem głównym, który pozwala klientowi zlokalizować dokładnie określony komponent. relacje - komponenty encyjne moga tworzyć relacje z innymi obiektami tego typu. 7/24
Trwałość komponentów encyjnych Komponenty sterowane komunikatami Istnieja dwa sposoby utrwalania danych przez komponenty encyjne: z trwałościa obsługiwana przez komponent (ang. bean-managed) - za zapis i odczyt wartości obiektu z bazy danych odpowiada sam komponent wykorzystujac do tego celu odpowienie biblioteki np. JDBC czy SQL, z trwałośćia obsługiwana przez kontener EJB (ang. container-managed) - za zapisywanie i odczytywanie stanu komponentu do/z bazy danych odpowiedzialny jest kontener EJB. Wszystkie konieczne metody odwołujace się do bazy danych sa automatyczne przez niego generowane. Komponenty sterowane komunikatami pozwalaja aplikacjom J2EE przetwarzać komunikaty w sposób asynchroniczny. Zwykle występuja one w roli odbiorcy komunikatu JMS, ale również może przetwarzać inne rodzaje wiadomości. Komunikaty wysyłane moga być przez dowolny inny element aplikacji J2EE - program klienta, inny komponent EJB czy też komponent sieciowy. Komunikat może być wysłany również przez aplikację JMS lub system niekorzystajacy z infrastruktury J2EE. Najbardziej widoczna różnica pomiędzy komponentem sterowanym komunikatami a pozostałymi komponentami polega na tym, że klient nie odwołuje się do komponentu sterowanego komunikatami za pomoca interfejsu. Serwery aplikacji 9/24 Ogólny proces tworzenia komponentu EJB Przykładami serwerów Java EE sa: GlassFish (glassfish.dev.java.net), JBOSS (www.jboss.org), Sun Java Web Server (www.sun.com), BEA Weblogic (www.beasys.com), Borland Visibroker (www.borland.com), Caucho Resin (www.caucho.com), IBM WebSphere (www.ibm.com), Jakarta Tomcat (jakarta.apache.org), Oracle Application Server (www.oracle.com), Orion (www.orionserver.com), W3 Jigsaw (www.w3.org), itd. Przygotowanie kodu źródłowego klasy Java, reprezentujacej komponent EJB Utworzenie dwóch interfejsów Java, reprezentujacych punkty kontaktu świata zewnętrznego z komponentem EJB: interfejs Home, służacy do zarzadzania cyklem życia komponentu EJB interfejs Remote/Local, służacy do wywoływania metod logiki biznesowej komponentu EJB Przygotowanie XML-owego pliku konfiguracyjnego - deskryptora instalacji (Deployment Descriptor) Kompilacja całego przygotowanego kodu Java i utworzenie z niego pliku EJB JAR Umieszczenie przygotowanych plików w systemie plików serwera aplikacji; zarejestrowanie komponentu EJB 11/24
omunikacja pomiędzy klientem a kontenerem Konwencja nazw komponentów EJB Składnik Składnia Przykład Klasa komponentu <nazwa>bean KalkulatorBean Interfejs domowy <nazwa>home KalkulatorHome Interfejs zdalny <nazwa> Kalkulator Lokalny interfejs domowy <nazwa>localhome KalkulatorLocalHome Interfejs lokalny <nazwa>local KalkulatorLocal 13/24 Struktura archiwum EJB JAR Przykład (1/10) Interfejs zdalny (Kalkulator): import javax.ejb.ejbobject; import java.rmi.remoteexception; public interface Kalkulator extends EJBObject { public double suma(double x1, double x2) throws RemoteException; 15/24
Przykład (2/10) Przykład (3/10) Interfejs domowy (KalkulatorHome): import java.rmi.remoteexception; import javax.ejb.createexception; import javax.ejb.ejbhome; public interface KalkulatorHome extends EJBHome { Kalkulator create() throws RemoteException, CreateException; Klasa komponentu EJB (KalkulatorBean): import javax.ejb.sessionbean; import javax.ejb.sessioncontext; public class KalkulatorBean implements SessionBean { public void ejbcreate() { public void ejbpostcreate() { public void ejbremove() { public void ejbactivate() { public void ejbpassivate() { public void setsessioncontext(sessioncontext sc) { public double suma(double x1, double x2) { return x1 + x2; 17/24 Przykład (4/10) Przykład (5/10) Plik MANIFEST-MF: Manifest-Version: 1.0 Plik jboss.xml: <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE jboss PUBLIC "-//JBoss//DTD JBOSS 3.0//EN" "http://www.jboss.org/j2ee/dtd/jboss_3_0.dtd"> <jboss> <enterprise-beans> <session> <ejb-name>kalkulator</ejb-name> <jndi-name>kalkulator/kalkulator</jndi-name> </session> </enterprise-beans> </jboss> Plik ejb-jar.xml: <?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> <description>kalkulator descr</description> <display-name>kalkulator EJB</display-name> <enterprise-beans> <session> <ejb-name>kalkulator</ejb-name> <home>org.j2eesamples.kalkulator.kalkulatorhome</home> <remote>org.j2eesamples.kalkulator.kalkulator</remote> <ejb-class>org.j2eesamples.kalkulator.kalkulatorbean</ejb-class> <session-type>stateless</session-type> <transaction-type>bean</transaction-type> </session> </enterprise-beans> </ejb-jar> 19/24
Przykład (6/10) Przykład (7/10) Instalujemy serwer aplikacji - w naszym przypadku jest serwer JBOSS w wersji 3.2.8 (rozpakowujemy plik jboss-3.2.8.sp1.tar.gz w katalogu domowym): > tar -zxvf jboss-3.2.8.sp1.tar.gz Uruchamiamy serwer aplikacji: > ~/jboss-3.2.8.sp1/bin/run.sh Kompilujemy pliki komponentu EJB: > javac -classpath.:~/jboss-3.2.8.sp1/client/jboss-j2ee.jar -d. KalkulatorHome.java > javac -classpath.:~/jboss-3.2.8.sp1/client/jboss-j2ee.jar -d. KalkulatorBean.java > javac -classpath.:~/jboss-3.2.8.sp1/client/jboss-j2ee.jar -d. Kalkulator.java Tworzymy katalog deploy w którym umieszczamy skompilowane pliki (katalog org). W katalogu deploy tworzymy katalog META-INF, w którym umieszczamy pliki MANIFEST-MF, ejb-jar.xml oraz jboss.xml. Tworzymy plik EJB-JAR: > jar -cvf kalkulator.jar * Umieszczamy utworzony plik kalkulator.jar w podkatalogu server/default/deploy: > cp kalkulator.jar ~/jboss-3.2.8.sp1/server/default/deploy 21/24 Przykład (8/10) Przykład (9/10) Kod klienta: Kod klienta (cd.): Object ref = jndicontext.lookup("kalkulator/kalkulator"); import javax.naming.initialcontext; import javax.rmi.portableremoteobject; import org.j2eesamples.kalkulator.kalkulator; import org.j2eesamples.kalkulator.kalkulatorhome; import java.util.hashtable; KalkulatorHome home = (KalkulatorHome) PortableRemoteObject.narrow(ref, KalkulatorHome.class); System.out.println("Jest obiekt domowy!"); Kalkulator kalkulator = home.create(); System.out.println(kalkulator.suma(2, 2)); class KalkulatorKlient { public static void main(string[] args) { try { Hashtable<String,String> props = new Hashtable<String,String>(); props.put(initialcontext.initial_context_factory, "org.jnp.interfaces.namingcontextfactory"); props.put(initialcontext.provider_url, "jnp://127.0.0.1:1099"); catch(exception e) { System.out.println(e.toString()); InitialContext jndicontext = new InitialContext(props); System.out.println("Jest kontekst!"); 23/24
Przykład (10/10) EJB 3.0 Kompilujemy kod klienta: > javac -classpath.:../../jboss-3.2.8.sp1/client/jboss-j2ee.jar -d. KalkulatorKlient.java Na koniec możemy uruchomić klienta: > java -cp.:../../jboss-3.2.8.sp1/client/jbossall-client.jar org.j2eesamples.kalkulator.kalkulatorklient Wraz z wersja EJB 3.0 (Java EE) nastapiło znaczne uproszczenie technologii EJB: mniejsza liczba plików źródłowych, dodatkowo sa one zwykłymi interfejsami i klasami Java, deskryptory xml nie sa obowiazkowe i zostały zastapione przez adnotacje, obiekty niezbędne do działania komponentu sa wstrzykiwane mechanizmem dependency injection, zamiast wyszukiwania ich przez JNDI, komponenty encyjne zostały tak dalece uproszczone, że przestały być komponentami EJB i stanowia odrębny standard o nazwie Java Persistance, oparty o odwzorowanie obiektowo-relacyjne. 25/24