Zdarzenia zwrotne i bezstanowe komponenty sesyjne
Transkrypt
Zdarzenia zwrotne i bezstanowe komponenty sesyjne
Zdarzenia zwrotne i bezstanowe komponenty sesyjne 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) 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 Klasa opisująca kartę kredytową package com.titan.processpayment; import java.util.Date; 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; } public CreditCardDO(String nmbr, Date exp, String typ) { number = nmbr; expiration = exp; type = typ; } 11 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; } 12 Obiekty dziedziny 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. 13 Wyjątek biznesowy package com.titan.processpayment; public class PaymentException extends java.lang.Exception { public PaymentException() { super(); } } public PaymentException(String msg) { super(msg); } 14 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 { ... } 15 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. 16 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> 17 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); 18 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(); } } 19 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); } } 20 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())); } } 21 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. Specyfikacja: http://java.sun.com/javaee/5/docs/api/javax/ejb/SessionContext.html 22 Kontekst sesji @Stateless public class A implements A_Remote { @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. 23 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(); ... } ... } 24 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(); } } ... } 25 Cykl życia komponentu bezstanowego Class.newInstance() @PostConstruct Nie istnieje @PreDestroy W puli gotowych komponentów Metoda biznesowa 26 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> 27 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> 28 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. 29 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. 30