Bezstanowe komponenty sesyjne i zdarzenia zwrotne 1. Zdarzenia zwrotne. klasy nasłuchujące, klasy nasłuchujące a dziedziczenie. 2. Bezstanowe komponenty sesyjne. interfejsy: bazowy, zdalny i lokalny, obiekty dziedziny, wyjątki biznesowe, właściwości środowiska, deskryptor wdrożenia, kontekst sesji i kontekst EJB, cykl życia komponentu bezstanowego i zdarzenia zwrotne. 1
Zdarzenia zwrotne Zmiany stanu komponentu encyjnego w trakcie jego cyklu życia powodują generowanie zdarzeń. Mogą one być przechwytywane przez odpowiednie metody. Poszczególne etapy cyklu życia encji są reprezentowane przez adnotacje: @javax.persistence.prepersist @javax.persistence.postpersist @javax.persistence.postload @javax.persistence.preupdate @javax.persistence.postupdate @javax.persistence.preremove @javax.persistence.postremove 2
Zdarzenia zwrotne @Entity public class Cabin {... @PostPersist void afterinsert( ) {... @PostLoad void afterloading( ) {... <entity class="com.titan.domain.cabin"> <post-persist name="afterinsert"/> <post-load name="afterloading"/> </entity> 3
Klasy nasłuchujące public class TitanAuditLogger { @PostPersist void postinsert(object entity) { System.out.println("Inserted entity: " + entity.getclass().getname( )); @PostLoad void postload(object entity) { System.out.println("Loaded entity: " + entity.getclass().getname( )); @Entity @EntityListeners({TitanAuditLogger.class, EntityJmxNotifier.class) public class Cabin {... @PostPersist void afterinsert() {... @PostLoad void afterloading() {... 4
Klasy nasłuchujące <entity class="com.titan.domain.cabin"> <entity-listeners> <entity-listener class="com.titan.listeners.titanauditlogger"> </entity-listener> <entity-listener class="com.titan.listeners.entityjmxnotifier"> <pre-persist name="beforeinsert"/> <post-load name="afterloading"/> </entity-listener> </entity-listeners> </entity> 5
Domyślne klasy nasłuchujące <entity-mappings> <entity-listeners> <entity-listener class="com.titan.listeners.titanauditlogger"> <post-persist name="afterinsert"/> <post-load name="afterloading"/> </entity-listener> <entity-listener class="com.titan.listeners.entityjmxnotifier"/> </entity-listeners> </entity-mappings> Wyłączenie domyślnej klasy nasłuchującej: @Entity @ExcludeDefaultListeners public class Cabin {... <entity class="com.titan.domain.cabin"> <exclude-default-listeners/> </entity> 6
Klasy nasłuchujące a dziedziczenie Klasy nasłuchujące są dziedziczone. Kolejność wykonywania metod nasłuchujących: 1. Metoda klasy nasłuchującej klasy bazowej. 2. Metoda klasy nasłuchującej klasy potomnej. 3. Metoda nasłuchująca klasy potomnej. Wyłączenie dziedziczenia klas nasłuchujących: @Entity @EntityListeners(TitanAuditLogger.class) public class Person {... @Entity @EntityListeners(EntityJmxNotifier.class) @ExcludeSuperclassListeners public class Customer extends Person {... 7
Komponenty sesyjne Komponenty sesyjne mają za zadanie modelowanie logiki aplikacji biznesowej. Opisują one przede wszystkim interakcje pomiędzy pozostałymi komponentami i przepływy zadań. Komponenty sesyjne mogą zarządzać danymi reprezentowanymi przez encje oraz bezpośrednio odwoływać się do bazy danych za pomocą języka QL lub SQL. Komponenty sesyjne dzielą się na bezstanowe i stanowe. Bezstanowe komponenty sesyjne są kolekcją wzajemnie powiązanych ale niezależnych usług reprezentowanych przez metody. 8
Interfejs biznesowy (bazowy) Jeśli chcemy, żeby interfejsy lokalny i zdalny udostępniały te same metody, warto rozważyć utworzenie jednego iterfejsu bazowego. package com.titan.processpayment; import com.titan.domain.customer; public interface ProcessPayment { public boolean bycheck(customer customer, CheckDO check, double amount) throws PaymentException; public boolean bycash(customer customer, double amount) throws PaymentException; public boolean bycredit(customer customer, CreditCardDO card, double amount) throws PaymentException; 9
Interfejs zdalny i lokalny package com.titan.processpayment; import javax.ejb.remote; @Remote public interface ProcessPaymentRemote extends ProcessPayment { package com.titan.processpayment; import javax.ejb.local; @Local public interface ProcessPaymentLocal extends ProcessPayment { 10
package com.titan.processpayment; import java.util.date; Przykład klasa opisująca public class CreditCardDO implements java.io.serializable { final static public String MASTER_CARD = "MASTER_CARD"; final static public String VISA = "VISA"; final static public String AMERICAN_EXPRESS = "AMERICAN_EXPRESS"; final static public String DISCOVER = "DISCOVER"; final static public String DINERS_CARD = "DINERS_CLUB_CARD"; public String number; public Date expiration; public String type; kartę kredytową public CreditCardDO(String nmbr, Date exp, String typ) { number = nmbr; expiration = exp; type = typ; 11
Przykład klasa opisująca czek package com.titan.processpayment; public class CheckDO implements java.io.serializable { public String checkbarcode; public int checknumber; public CheckDO(String barcode, int number) { checkbarcode = barcode; checknumber = number; Klasy CreditCardDO oraz CheckDO są obiektami dziedziny (domain object) Obiekty tych klas są wykorzystywane w roli mediów do przesyłania danych. Ich zastosowanie upraszcza ponadto wykorzystywane w aplikacji interfejsy. 12
Przykład wyjątek biznesowy package com.titan.processpayment; public class PaymentException extends java.lang.exception { public PaymentException() { super(); public PaymentException(String msg) { super(msg); 13
Przykład komponent sesyjny package com.titan.processpayment; import... @Stateless public class ProcessPaymentBean implements ProcessPaymentRemote, ProcessPaymentLocal {... Inny sposób przekazania informacji o implementowanych interfejsach: @Stateless @Local(ProcessPaymentLocal.class) @Remote(ProcessPaymentRemote.class) public class ProcessPaymentBean {... 14
Przykład właściwości środowiskowe final public static String CASH = "CASH"; final public static String CREDIT = "CREDIT"; final public static String CHECK = "CHECK"; // mechanizm wstrzykiwania dla zasobów @Resource(mappedName = "titandb") DataSource datasource; @Resource(name = "min") int minchecknumber; Zmienne titandb i min są utworzone i zarządzanie przez kontener EJB w ramach tzw. kontekstu nazewnictwa ENC (Enterprise Naming Contex). Komponent sesyjny może uzyskać dostęp do takich zasobów poprzez mechanizm wstrzykiwania. 15
Przykład deskryptor wdrożenia <ejb-jar xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/xmlschema-instance" xsi:schemalocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/ejb-jar_3_0.xsd" version="3.0"> <enterprise-beans> <session> <ejb-name>processpaymentbean</ejb-name> <env-entry> <env-entry-name>min</env-entry-name> <env-entry-type>java.lang.integer</env-entry-type> <env-entry-value>250</env-entry-value> </env-entry> </session> </enterprise-beans> </ejb-jar> 16
Przykład komponent sesyjny private boolean process(int customerid, double amount, String type, String checkbarcode, int checknumber, String creditnumber, java.sql.date creditexpdate) throws PaymentException{ Connection con = null; PreparedStatement ps = null; try { con = datasource.getconnection(); ps = con.preparestatement("insert INTO payment" + "(customer_id, amount, type, check_bar_code," + "check_number, credit_number, credit_exp_date)" + "VALUES (?,?,?,?,?,?,?)"); ps.setint(1, customerid); ps.setdouble(2, amount); ps.setstring(3, type); ps.setstring(4, checkbarcode); ps.setint(5, checknumber); ps.setstring(6, creditnumber); ps.setdate(7, creditexpdate); 17
Przykład komponent sesyjny if (ps.executeupdate()!= 1) { throw new EJBException("Payment insert failed"); return true; catch (SQLException sql) { throw new EJBException(sql); finally { try { if (ps!= null) ps.close(); if (con!= null) con.close(); catch (SQLException se) { se.printstacktrace(); 18
Przykład komponent sesyjny public boolean bycash(customer customer, double amount) throws PaymentException { return process(customer.getid(), amount, CASH, null, -1, null, null); public boolean bycheck(customer customer, CheckDO check, double amount) throws PaymentException { if (check.checknumber > minchecknumber) { return process(customer.getid(), amount, CHECK, check.checkbarcode, check.checknumber, null, null); else { throw new PaymentException( "Check number is too low. Must be at least " + minchecknumber); 19
Przykład komponent sesyjny public boolean bycredit(customer customer, CreditCardDO card, double amount) throws PaymentException { if (card.expiration.before(new java.util.date())) { throw new PaymentException("Expiration date has passed"); else { return process(customer.getid(), amount, CREDIT, null, -1, card.number, new java.sql.date(card.expiration.gettime())); 20
Kontekst sesji @Stateless public class ProcessPaymentBean implements ProcessPaymentLocal { @Resource SessionContext ctx;... Interfejs javax.ejb.sessioncontex jest dostępny w formie zasobu i pozwala na uzyskanie dodatkowych informacji poprzez metody: GetEJBLocalObject() - zgodność z EJB 2.1, GetEJBObject() - zgodność z EJB 2.1, GetMessageContext() - kontekst w przypadku wywołania jako webservice, getbusinessobject(class<t>) - obiekt biznesowy, getinvokedbusinessinterface() - wywołany interfejs. Specyfkacja: http://java.sun.com/javaee/5/docs/api/javax/ejb/sessioncontext.html 21
Kontekst sesji @Stateless public class A implements ARemote { @Resource private SessionContext cx;... public void somemethod( ) { BRemote b =... // referencja do interfejsu Remote // komponentu B. ARemote myself = cx.getbusinessobject(aremote.class); b.metoda( myself ); Przykład pokazuje jak przekazać innym komponentom referencję do siebie. Bezpośrednie przekazanie referencji this jest niepoprawne, ponieważ komponenty są udostępniane poprzez interfejsy, a zarządzanie konkretnymi obiektami pozostaje w gestii kontenera. 22
Kontekst EJB Interfejs SessionContex rozszerza EJBContex, który pozwala otrzymać informacje zarówno o kliencie jak i środowisku kontenera - http://java.sun.com/javaee/5/docs/api/javax/ejb/ejbcontext.html. Przykłady: @Stateless public class BankBean implements Bank { @Resource SessionContext cx;... public void withdraw(int acctid, double amount) throws AccessDeniedException { String modifiedby = cx.getcallerprincipal().getname();...... 23
Kontekst EJB @Stateless public class BankBean implements Bank { @Resource SessionContext cx;... public void withdraw(int acctid, double amount) throws AccessDeniedException { if (amount > 10000) { boolean ismanager = cx.iscallerinrole("manager"); if (!ismanager) { // Tylko Manager może wypłacić taką kwotę throw new AccessDeniedException();... 24
Cykl życia komponentu bezstanowego Class.newInstance() @PostConstruct Nie istnieje @PreDestroy W puli gotowych komponentów metoda biznesowa 25
Tworzenie komponentu @Stateless public class MyBean implements MyLocal { @PostConstruct public void myinit() {... <ejb-jar> <enterprise-beans> <session> <ejb-name>mybean</ejb-name> <post-construct> <lifecycle-callback-method> myinit </lifecycle-callback-method> </post-construct> </session> </enterprise-beans> </ejb-jar> 26
Usuwanie komponentu @Stateless public class MyBean implements MyLocal { @PreDestroy public void cleanup() {... <ejb-jar> <enterprise-beans> <session> <ejb-name>mybean</ejb-name> <pre-destroy> <lifecycle-callback-method> cleanup </lifecycle-callback-method> </pre-destroy> </session> </enterprise-beans> </ejb-jar> 27
Cykl życia komponentu bezstanowego Zarówno metoda @PostConstruct jak i @PreDestroy są wywoływane tylko raz w trakcie życia komponentu niezależnie od liczby obsłużonych przez niego żądań klientów. W trakcie wywoływania tych metod dostępne są wszelkie zasoby udostępniane przez kontener. 28
Podsumowanie Kontener komponentów EJB poza podstawowymi funkcjami związanymi z zarządzaniem komponentów udostępnia programiście wiele dodatkowych możliwości jak np. dostęp do zasobów, konteksty oraz kontrolowanie stanu obiektów i reagowanie na jego zmiany. 29