J2EE Session Beans Entity Beans (BMP) Repetisjon TLSB (tilsandsløse Session Beans). Husk: Remote-interface => EJB-objekt gethelloworldstring() Home-interface => home-objekt create() Implementasjonsklasse ejbcreate() => kalles av klient via create i home-objektet ejbremove() ejbactivate() ejbpassivate() setsessioncontext(sessioncontext ctx) gethelloworldstring => kalles av klient via EJB-objektet setsessioncontext() kalles ved opprettelse av implementasjonsobjekt kun en gang! ejbcreate() kan kalles flere ganger på samme implementasjonsobjekt! Fullstendig skisse JNDI og EJB //gjør JNDI-lookup for å finne home-objektet //dette må castes til riktig interface HelloWorld hello = home.create(); 1 2 String str = hello.gethelloworldstring (); 5 6 Container 3 HelloWorldHomeobjekt 4 EJBObject (for HelloWorld) HelloWorldBean HelloWorldBean HelloWorldBean Når skal finne en EJB skal du ikke gjøre Context kontekst = new InitialContext(); Object obj = kontekst.lookup( Hello ); Blir problemer hvis du kjøper to komponenter som bruker samme JNDI-navn (navnekonflikt)! Hver EJB-applikasjon har sin egen JNDI-kontekst, gjør slik: Context kontekst = new InitialContext(); Object obj = kontekst.lookup( java:comp/env/ejb/helloworldexample ); java:comp/env/ejb/helloworldexample mappes til et globalt JNDInavn for eksempel Hello det globale navnet bestemmes i deploytool og kan skiftes etter behov merk at det nå ikke er noe problem med navnekonflikter fordi vi kan endre det globale navnet til applikasjonen ved behov => trenger ikke å endre navnet brukt internt i applikasjonen. EJBObject (for HelloWorld) gir beskjed hvilken applikasjon det er snakk om HelloWorldapplikasjon Hello lookup( java:comp/ env/ejb/helloworld Example ); HelloWorld-applikasjonen: ejb/helloworldexample mappes til Hello Mapping for andre applikasjoner: ejb/ Tjener
GeneriskSessionBean import javax.naming.*; public class GeneriskSessionBean implements SessionBean{ private String klassenavn = ""; private boolean debug = true; protected SessionContext context; protected void setklassenavn(string navn){ klassenavn = navn; protected void setdebug(boolean debug){ this.debug = debug; protected void log(string str){ if(debug) System.out.println(klassenavn + " " + str); public void ejbremove(){ log("ejbremove()"); public void ejbactivate(){ log("ejbactivate()"); public void ejbpassivate(){ log("ejbpassivate()"); public void setsessioncontext(sessioncontext ctx){ InitialContext init = new InitialContext(); Boolean tmp = (Boolean)init.lookup("java:comp/env/debug"); debug = tmp.booleanvalue(); catch(namingexception e){ debug = true; log("setsesisoncontext()"); context = ctx; Miljøvariablen debug i GeneriskSessionBean Pool av sobjekter spiller ingen rolle hvilket objekt som gjør jobben skalerbarhet! (trenger kanskje 10 objekter for 100 klienter TLSB kan ikke ha tilstander!! Session Beans med tilstander 2: økteller() EJBobjekt Container 2: økteller () TFSB (Tilstandsfull Session Bean) Har tilstand på vegne av klient Et sobjekt pr. klient Når klient kaller create() opprettes et nytt EJBimplementasjonsobjekt. Ser ut som TLSB, kun et valg i dd som avgjør hvordan EJB behandles av container (se neste foil). Påvirker skalerbarhet! 1000 klienter fører til 1000 implementasjonsobjekter! Brukes kun når nødvendig. Pool av 4 tilstandsløse Session Beans
TLSB/TFSB og deploytool TFSB Container 2: økteller() 2: økteller() EJBobjekt 1 Session Bean for hver klient Passivisering/aktivering av TFSB TLSB => container styrer pool av instanser (hvor mange) TFSB => klienter styrer hvor mange instanser som må lages! Ikke sikkert at det er nok minne til alle instansene Kanskje må lagres på disk! Passivisering => container kaller ejbpassivate() Hentes fra disk Aktivering => container kaller ejbactivate() Alt som skal kunne passiviseres til disk må kunne serialiseres!! Eksempel aktivering/passivering En EJB som gjør noe mot en DB import java.sql.*; public class EksempelBean implements GeneriskSessionBean{ Connection forbindelse = null; //holder oppkobling mot DB slik at vi ikke trenger langsom oppko bling //ved hver spørring mot DB. public void ejbcreate() throws CreateException{ lagdbconnection(); //lager oppkobling mot Database catch(exception e){ //unntaksbehandling public void gjørnoemeddatabase(){ Statement setning = forbindelse.createstatement(); //gjør spørring mot database catch(exception e){ //unntaksbehandling forts. eksempel Passivisering public void ejbpassivate(){ if (forbindelse!= null) forbindelse.close(); catch(exception e){ //unntakshåndtering forbindelse = null; public void ejbactivate(){ logdbconnection(); catch(exception e){ //unntakshåndtering private void lagdbconnection() throws SQLException{ String databasedriver = "oracle.jdbc.driver.oracledriver"; Class.forName(databasedriver); forbindelse = DriverManager.getConnection(...); Databaseoppkoblinger er ikke serialiserbare og må passiviseres dvs. Connection-objekter må settes til null gjelder også andre ikke-serialiserbare objekter Bruk DataSource-objekter - serialiserbare Referanser til andre EJB er er også serialiserbare. Vanligvis trenger vi ikke gjøre noe i ejbactivate()/ejbpassivate().
Unntak Applikasjonsunntak Unntak som vi kan komme oss ut av Gir for eksempel brukeren mulighet til å håndtere feilen public ArrayList getkategori(string kategori) throws FeilKategoriException; Hvis bruker oppgir en kategori som ikke eksisterer hiv unntaket FeilKategoriException; bruker får beskjed om feilmeldingen og kan velge kategori på nytt Unntak 2 Systemunntak unntak vi typisk ikke kommer oss ut av hva gjør du om du ikke finner databasen? EJBException() => runtime => Trenger ikke å anngi noe throws uttrykk Blir opp til container å vurdere hva som skal returneres til klient public enellerannenmetode(){ //lag oppkobling mot en database //gjør en spørring mot databasen //gjør noe med dataene fra databasen catch(sqlexception e){ System.out.println( Unntak i enellerannenmetode() + e); throw new EJBException(e); Varig lagring av data Typisk brukes database til lagring av varige data. Applikasjon for overføring mellom konto => Endringer i en databasetabell Kontonr 1720 200000 1720 200001 Saldo 1000 100 Muligheter for overføring Lag en Session Bean KontoOverføring med metoden: public void overfør(int frakonto, int tilkonto, int beløp){ //hent oppkobling til databasen //trekk beløp fra frakonto i DB //sett inn beløp på tilkonto i DB Kommentarer til løsningen: Løsningen strider mot objektorientert tankegang. Hvor er konto-objektet? Vi ønsker nå et objekt som representerer en rad i databasetabellen. Endringer i objektet skal også føre til endringer i databasen. Forskjell mellom Entity og Session Beans overfør() overfør() overfør(..){.. overfør(..){.. KontoOverføring (SB) KontoOverføring (SB) taut() settinn() trekk beløp sett inn beløp KontoEJB KontoEJB trekk beløp sett inn beløp Kontonr 1 2 Kontonr 1 2 Saldo 100 200 Saldo 100 200 Session Beans representerer prosessering Entity Beans representerer data Eksempel; bokhandel En bokhandel har bøker (data) => representeres med en Entity Bean BokEJB For å ta i mot bestillinger på bøker må man prosessere forespørsler => Session Bean gjør prosesseringen. Entity Beans er ikke veldig ulike Session Beans i implementasjonen samme interface Implementasjonsklasse med noen andre metoder. Må velge Entity Bean i deploytool
KontoEJB Synkroniseringsmetoder Representerer en rad i tabellen Konto public class KontoBean implements EntityBean{ private int kontonr; //trenger egentlig ikke denne private int saldo; //får verdien til en av raden i DB-tabellen EJB-objekt EJBimplementasjonsobjekt public void settinn(int beløp){ saldo += beløp; //øker saldo public void taut(int beløp){ saldo -= beløp; //minker saldo public void ejbload(){ saldo = //hent via SQL saldo fra DB () public void ejbstore(){ //sett saldo i DB til å være lik saldo //saldo er varig lagret 1: taut() 2: ejbload() 3: taut() 4: ejbstore() Remote-interfacet import java.rmi.*; public interface KontoBMP extends EJBObject{ public void settinn(int kroner) throws RemoteException; public void taut(int kroner) throws RemoteException, KontoException; public int hentsaldo() throws RemoteException; //merk at det ikke er noen metode for å hente primærnøkkelen kontonr //denne metoden arves fra EJBObject og er derfor bestandig tilgjengelig! import java.rmi.*; import java.util.*; Home-interfacet public interface KontoBMPHome extends EJBHome{ public KontoBMP create(integer nyttkontonr, String fornavn, String etternavn) throws RemoteException, CreateException; //finn metoder public KontoBMP findbyprimarykey(integer kontonr) throws RemoteException, FinderException; public Collection findbysaldooverbeløp(int saldo) throws RemoteException, FinderException; public Collection findallekontoer() throws RemoteException, FinderException; //home-metoder public int finntotaltantallkontoer() throws RemoteException; //home-metode finnmetoder create()- lager en ny rad i en tabell For å endre data som ligger i DB må man finne EJB-instansen for raden. findbyprimarykey()/ejbfindbyprimarykey() findallkontoer()/ejbfindallekontoer() public static void main(){ KontoBMPHome home = //JNDI-lookup KontoBMP konto = home.findbyprimarykey(new Integer(1)); konto.settinn(100); Collection kontoer = home.findallekontoer(); //har nå en liste med referanse til alle kontoene Implementasjon ejbfindallekontoer() public Collection ejbfindallekontoer() throws FinderException{ String sql = "select kontonr from konto"; ArrayList kontoer = new ArrayList(); Connection forbindelse = null; PreparedStatement prep = null; ResultSet res = null; forbindelse = ds.getconnection(); prep = forbindelse.preparestatement(sql); res = prep.executequery(); while (res.next()){ int tmpnøkkel = res.getint("kontonr"); kontoer.add(new Integer(tmpNøkkel)); catch(sqlexception e){ log("unntak " + e); throw new EJBException(e); finally{ lukkforbindelser(res,prep,forbindelse); return kontoer;
Pool av Entity Beans Pool av Entity Beans. Merk at containeren selv kan bestemme hvor mange Entity sobjekter som er nødvendig. Kan ha 100 klienter (kontoklienter) og bare 10 EJBimplementasjonsobjekter Når en klient skal endre konto med kontonr 1 så sørger containeren for å fylle et av implementasjonsobjektene med verdiene fra rad 1 i DB-tabellen via ejbload(). EJB-objektene finnes for eksempel via findbyprimarykey(). 1 bruker konto med kontonr 1 osv. 1 2 taut() EJB-obj 1 EJB-obj 2 Spiller ingen rolle hvilket EJBimpl. obj som brukes. Det vil alltid fylles med riktig verdi. EJB-impl. obj. 1 EJB-impl. obj. 2 EJB-impl. obj. 3 n EJB-obj n EJB-impl. obj. 4 Entity Beans og nettverkstrafikk Fasade av Session Beans fjernaksess Bok 1 fjernaksess Bok 1 Bok 2 Bok 2 Endre pris på alle bøker + 10 % Session Bean Bok 100 Bok 100 Bruker lokale interface til Entity Bean Bok => mindre overhead når man ikke bruker nettverksstakken.