EJB 3
Tartalom
Az EJB 2.1 problémái Az EJB 3 megoldásai
Miért nem szeretik sokan az EJB 2.1-et?
bonyolult a fejlesztés:
sok file (legalább 3 java + legalább 2 xml), a fejlesztı eszközök varázslóival kell karbantartani a bean osztálynak implementálnia kell a javax.ejb.Session/Entity/MessageDrivenBean interfészt callback metódusokat definiál, amiket nem használunk mindig, mégis implementálni kell egy EJB metódus meghívása több sornyi kód:
home interfész megkeresése a JNDI API-val home interfész create() metódusának hívása különféle Exception-ök kötelezı kezelése
entity beanek használata esetén: a session facade tervezési minta miatt DTO osztályok
Miért nem szeretik sokan az EJB 2.1-et?
bonyolult a telepítés:
telepítésleíró (XML fájl) karbantartása konténer specifikus fájlok karbantartása
bonyolult tesztelés:
konténeren belül: gyakorlatilag egy másik komponens (Servlet/JSP) megírását igényli konténeren kívül: csak lokális interfészes EJB-hez emiatt írjunk remote-ot is?
Miért nem szeretik sokan az EJB 2.1-et?
bonyolult debuggolás:
server log fájljainak analizálása: nem hatékony nagy fájlok esetén az alkalmazás szervert futtató JVM-hez csatlakozni debuggerrel: nem minden alkalmazás szerverhez van ilyen támogatás
Miért használjunk mégis az EJB-ket?
Az üzleti logikát meg lehet hagyományos Java osztályokban is valósítani, de érdemes az EJB-ket használni, ha: az üzleti logika távoli elérése szükséges (más JVM-bıl, esetleg más géprıl) rugalmas middleware szolgáltatások: tranzakciókezelés (akár elosztott) a metódusokhoz való hozzáférést deklaratívan akarjuk szabályozni öröklött alkalmazásokkal kell együttmőködni követelmény a skálázhatóság O-R leképezést akarunk megvalósítani (bár erre voltak korábban is Java SE technológiák: JDO, Hibernate) elıírják
EJB 3 célja
az EJB architektúra egyszerősítése a fejlesztı szempontjából az eddig nyújtott szolgáltatások természetesen megmaradnak + számos új feature
Mik az egyszerősítések?
sok file spóroljunk velük:
a telepítésleíró helyett ún. annotációkkal defináljuk a forráskódban amit eddig a telepítésleíróban tettünk ráadásul ezeket az annotációkat is csak akkor kelljen megadni, ha eltérünk a defaulttól (ehhez persze specifikálni kell, mi a default) home interfész elhagyása session beaneknél az entity beanek helyett POJO perzisztencia
a perzisztens entitásoknál kiesik a remote/local interfész is DTO osztályok elhagyhatók (+sok egyéb feature)
Mik az egyszerősítések?
sok kódsor ne legyen kötelezı
nem kell a javax.ejb.EnterpriseBean interfészeket implementálni (ha valamelyik callback metódus szükséges, annotációval jelöljük meg) EJB meghívásának egyszerősítése:
a home interfész kiküszöbölése JNDI keresés kihagyása, helyette külsı erıforrások injektálása Minden kivétel javax.ejb.EJBException-be csomagolva dobódjon tovább (ez egy RuntimeException, így nem kötelezı elkapni)
Annotációk
A Java SE 5 újítása Az EJB 3 erısen épít rá A forráskódba (osztály, metódus, tagváltozó elé) illesztett metaadat Közvetlenül nem a program jelentését módosítja, hanem azt, hogy különbözı eszközök és libraryk hogyan kezeljék (ez persze végül a futási idejő mőködésre hatással van) Korábban:
komment javadoc XDoclet
A Java 5 egységes szintaxist határoz meg,és standard módszert saját annotációk definiálására
Annotációk – szintaxis
@AnnotációNeve(paraméter1=érték1, paraméter2=érték2...) ha csak egy paraméter van: @AnnotációNeve(érték) is lehetséges ha nem adunk át paramétert: @AnnotációNeve, vagy @AnnotációNeve() A paraméter tömb is lehet, ekkor értéke a szokásos tömb szintaxis {elem1, elem2,...}
Beépített annotációk
a Java SE 5 compilere 3 annotációt ismer alapból:
@Override: gépelési hibák elkerülése @Deprecated: elavult metódusok megjelölésére (korábban commentben) @SurpressWarnings: különféle warningok elrejtését végzi
Saját annotációk
@interface interfészben definiáljuk az annotáció nevét, és hogy milyen paramétereket fogad el a paraméterek metódus szintaxissal adhatók meg ha csak egy paraméter van, annak value() nevet kell adni lehetséges visszatérési érték:
egyszerő típus String Class Annotation enum ezek tömbjei
lehet default értékük
Példa (egyszerő dokumentáció) import hu.bme.aait.TODO ... @TODO(item=”Javítani”) public void myMethod{...}
package hu.bme.aait; public @interface TODO{ public enum Severity { CRITICAL, IMPORTANT, TRIVIAL, DOCUMENTATION }; Severity severity() default Severity.IMPORTANT; String item(); }
Példa annotáció futási idejő feldolgozására public class Foo { @Test public static void m1() {...} public static void m2() {...} } public class RunTests { public static void main(String[] args){ int passed = 0, failed = 0; for (Method m:Class.forName(args[0]).getMethods()) { if (m.isAnnotationPresent(Test.class)) { try { m.invoke(null); passed++; } catch (Throwable ex){ System.out.printf("Test %s failed: %s %n", m,ex.getCause()); failed++; } } } System.out.printf("Passed: %d, Failed %d%n", passed, failed); } }
Meta-annotációk Annotációk annotálására Annotáció definiálásánál egyéb részletek megadására használatos: @Target: megadhatjuk, mire alkalmazható az annotáció, pl.: @Target({ElementType.METHOD, ElementType.CONSTRUCTOR}) public @interface Test{}
Meta-annotációk
@Retention: hogyan kezelje a complier az annotációt? Lehetséges értékek:
RetentionPolicy.SOURCE: a fordító nem fordítja le RetentionPolicy.CLASS: a fordító belerakja a class fájlba, de a JVM ignorálja futás közben RetentionPolicy.RUNTIME: futás közben beolvassa a JVM a class fájlból
Meta-annotációk
@Documented: az annotáció kerüljön bele a javadoc-ba (enélkül nem kerül bele) @Inherited: az ezzel megjelölt annotációval annotált osztály a gyermekosztályaira is örökíti az annotációt
Java EE annotációk
A Java EE 5 egy sor annotációt definiál, az alkalmazás szerver gyártó feladata ezeket az annotációkat értelmezni a komponensek telepítéskor, és a komponens fejlesztıje által annotációk által definiált futási idejő szolgáltatásokat biztosítani Vagyis a telepítésleírók szerepét veszi át Alapelv: a telepítésleírók továbbra is használhatók, és felüldefiniálják az annotációkat, ha az is van (ez különösen fontos, ha harmadik fél számára újrafelhasználhatóvá akarjuk tenni)
EJB 3 session bean
2 fájl már elég:
Enterprise bean osztály business interfész
ráadásul ezeknek ne kelljen semmilyen ejb-specifikus interfészt megvalósítaniuk (POJO-k = Plain Old Java Object) Meg kell oldani:
a 2 fájl összerendelését (nincs DD), állapotkezelés definiálását (nincs DD) a callback metódusok definiálását (nem implementálunk ejbspecifikus IF-eket) inicializálást, referencia szerzését (nincs home IF, és nem akarunk JNDI hívásokat sem)
Megoldások
bean osztály és business IF összerendelése:
az implements kulcsszó elég, ha csak egy interfészt implementál a bean osztály lokális interfésznek tekintjük, hacsak nem rakunk @Remote annotációt az IF vagy a bean osztályhoz ha több interfészt implementál: a @Local illetve a @Remote paraméterében adható meg, hogy ezek közül melyik része a lokális/remote interfésznek a java.io.Serializable, Externalizable, és a javax.ejb package interfészei nem számítanak bele
Megoldások
állapotkezelés megadása:
a bean osztályra @Stateful vagy @Stateless megadása jelentésük ugyanaz mint 2.1-ben (ugyanolyan életciklust biztosít számukra a konténer)
Megoldások
callback metódusok:
a bean osztály nem implementál interfészeket, helyette megjelöl metódusokat, ha kezelni akar bizonyos eseményeket (ezek a metódusok más osztályban is lehetnek, lásd késıbb az Interceptoroknál) ejbCreate() @PostConstruct ejbAcitvate() @PostActivate ejbPassivate() @PrePassivate ejbRemove()
@Remove (ezt a kliens hívja, ha állapottal rendelkezı) @PreDestroy: ezt a konténer hívja eltávolítás elıtt
Megoldások
referencia szerzés az EJB hívásához:
telepítéskor nem muszáj JNDI nevet adni az EJB-nek, mert a teljesen kvalifikált nevét kapja defaultként (ha mégis adunk:@Stateful(name=”ejb/MyName”)) ha a kliens konténerben fut (pl. más EJB vagy web komponens): függıség injektálás (dependency injection) @EJB annotációval konténeren kívüli kliens: JNDI lookup marad, de a keresés eredménye egybıl referencia a beanre, nemcsak a home hasonló függıség injektálás más erıforrásokra is (pl. adatbázis, JMS üzenetsor, EJB context): @Resource
Interceptorok
Aspektus orientált programozás:
a teljes alkalmazásra kiterjedı szempontok (aspektusok), pl. loggolás, biztonsági ellenırzés végigvezetése a teljes alkalmazáson nehézkes lehet csak OO technikákkal AOP alapgondolata: ezeket valahol külön implementáljuk, a kódban valahogyan definiáljuk, hova kerüljenek be, és egy átszövı (weaver) segítségével álljon elı a kész program általában meglévı nyelvek kiterjesztésével támogatják az AOP-ot
Interceptorok
Az EJB 3 által definiált interceptorok az AOP kezdetleges támogatásának tekinthetık session és message-driven beanekre definiálhatók, vagy egyes metódusokra megszakítják a bean metódusát, módosíthatják a bemenı paramétereket, akár meg is akadályozhatják a tényleges meghívást (Servlet Filterekhez hasonló)
Interceptorok hozzáadása az EJB-hez
@Interceptor({Interceptor1.class) mivel egy annotáció csak egyszer szerepelhet, ha többet akarunk: @Interceptors({Interceptor1.class, Interceptor2.class, ...}) ez általános elv annotációknál a lista sorrendjében hívódnak meg az interceptorok mint mindent, ezt is meg lehet adni DD-ben is
Interceptor írása
tetszıleges Java osztály, paraméter nélküli konstruktorral az interceptor metódus
@AroundInvoke-kal van annotálva, dobhat Exception-t használhat függıség injektálást a hívott EJB biztonsági és tranzakciós kontextusában fut bemenı paramétere egy InvocationContext, ezen keresztül éri el a megszakított metódust, paramétereket, a bean példányt, és ebbe rakhat névvel azonosított adatokat más interceptorok számára
ha egy EJB callback metódusát külön osztályba rakjuk, akkor azt nem @AroundInvoke-kal, hanem a @PostConstruct, @PrePassivate, stb. annotációkkal kell megjelölni