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