J2EE og distribuerte systemer Leksjon 9: Session Beans



Like dokumenter
Repetisjon J2EE. Fullstendig skisse. JNDI og EJB. Session Beans Entity Beans (BMP)

J2EE. CMP Entity Beans, Transaksjoner, JSP

J2EE og distribuerte systemer Leksjon 10: Entity Beans (BMP)

J2EE. Katalogtjenester, JNDI og Enterprise Beans

Løsningsskisse, eksamen J2EE og distribuerte systemer 19.mai 2004

J2EE og distribuerte systemer Leksjon 11: Entity Beans (CMP)

HØGSKOLEN I SØR-TRØNDELAG

Videregående programmering 6

Eksamen i Internetteknologi Fagkode: ITE1526

HØGSKOLEN I SØR-TRØNDELAG

Installasjonsveiledning

J2EE og distribuerte systemer Leksjon 7: Installasjon av applikasjonstjener og JNDI

2 Om statiske variable/konstanter og statiske metoder.

JSP - 2. Fra sist. Hvordan fungerer web? Tjenerside script HTML. Installasjon av Web-tjener Et enkelt JSP-script. Ønsker dynamiske nettsider:

Løsningsforslag Test 2

TDT4100 Objektorientert programmering

J2EE og distribuerte systemer Leksjon 8: Oppbygning av J2EE-applikasjoner

Løsningsforslag ukeoppg. 6: 28. sep - 4. okt (INF Høst 2011)

TOD063 Datastrukturer og algoritmer

Algoritmer og datastrukturer Kapittel 3 - Delkapittel 3.1

Eksamen. Objektorientert Programmering IGR 1372

HØGSKOLEN I SØR-TRØNDELAG

Løse reelle problemer

IN1000 Obligatorisk innlevering 7

INF1010 våren 2019 Onsdag 30. januar. Mer om unntak i Java (med litt repetisjon av I/O først)

Forelesning inf Java 4

i=0 Repetisjon: arrayer Forelesning inf Java 4 Repetisjon: nesting av løkker Repetisjon: nesting av løkker 0*0 0*2 0*3 0*1 0*4

Argumenter fra kommandolinjen

Løse reelle problemer

INF1010 våren 2017 Onsdag 25. januar. Litt om unntak i Java

Introduksjon til objektorientert. programmering. Hva skjedde ~1967? Lokale (og globale) helter. Grunnkurs i objektorientert.

UNIVERSITETET I OSLO

Leksjon 7. Filer og unntak

Hva er verdien til variabelen j etter at følgende kode er utført? int i, j; i = 5; j = 10; while ( i < j ) { i = i + 2; j = j - 1; }

HØGSKOLEN I SØR-TRØNDELAG

Installere JBuilder Foundation i Windows XP

Enkle generiske klasser i Java

INF1000 Metoder. Marit Nybakken 16. februar 2004

En algoritme for permutasjonsgenerering

Eksekveringsrekkefølgen (del 1) Oppgave 1. Eksekveringsrekkefølgen (del 2) Kommentar til oppgave 1. } // class Bolighus

BOKMÅL Side 1 av 7. KONTINUASJONSEKSAMEN I FAG TDT4100 Objektorientert programmering / IT1104 Programmering, videregående kurs

Forklaring til programmet AbstraktKontoTest.java med tilhørende filer Konto.java, KredittKonto.java, SpareKonto.java

Tilkobling og Triggere

PG4200 Algoritmer og datastrukturer Lab 1. 8.januar I dag skal vi undersøke en rekke velkjente databeholdere i Java:

INF 1010, vår 2005 Løsningsforslag uke 11

Kapittel 8: Programutvikling

Jentetreff INF1000 Debugging i Java

UNIVERSITETET I OSLO

programeksempel Et større En større problemstilling Plan for forelesingen Problemstillingen (en tekstfil) inneholdt ordet "TGA"

INF1000 Prøveeksamen Oppgave 7 og 9

NB!!! Veldig korte svar er gitt her. Disse burde det vært skrevet mer på ved en eksamen..

INF Løsning på seminaropppgaver til uke 8

Hva er Derby og Java DB? Denne forelesningen. Java Database Connectivity (JDBC) Hva er Derby og Java DB?

Bli Kjent med Datamaskinen Introduksjon ComputerCraft PDF

Å bruke Java API-et til å sortere tabeller/arraylister der elementene er (referanser til) objekter

INF1000 HashMap. Marit Nybakken 2. november 2003

Array&ArrayList Lagring Liste Klasseparametre Arrayliste Testing Lenkelister

Fra Python til Java, del 2

Del 3: Evaluere uttrykk

INF Notater. Veronika Heimsbakk 10. juni 2012

Å lese tall fra en fil, klassen Scanner

Algoritmer og datastrukturer E Løkker i Java

Oblig 4Hybelhus litt mer tips enn i oppgaven

INF Seminaroppgaver til uke 3

Kapittel 9: Sortering og søking Kort versjon

INF1000 : Forelesning 4

La oss først se på problemet med objektorientert tankegang. Se figuren under. Konto

INF1010 våren 2018 tirsdag 23. januar

UNIVERSITETET I OSLO

i=0 i=1 Repetisjon: nesting av løkker INF1000 : Forelesning 4 Repetisjon: nesting av løkker Repetisjon: nesting av løkker j=0 j=1 j=2 j=3 j=4

Dagens forelesning. Java 13. Rollefordeling (variant 1) Rollefordeling (variant 2) Design av større programmer : fordeling av roller.

INF Uke 10. Ukesoppgaver oktober 2012

UNIVERSITETET I OSLO

Dagens tema: 12 gode råd for en kompilatorskriver. Sjekking av navn. Lagring av navn. Hvordan finne et navn?

Kapittel 9. Distribusjon. Fjernbruker. Tjenermaskin LAN WAN. Nærbruker. Figur 9-1: En enkel klient/tjener distribusjon

praktiske eksempler DOM Document Object Model DOM og Høst 2013 Informasjonsteknologi 2 Læreplansmål Gløer Olav Langslet Sandvika VGS

Programmeringsspråket C

Dagens tema: 12 gode råd for en kompilatorskriver

TDT4100 Objektorientert programmering

Fra Python til Java. En introduksjon til programmeringsspråkenes verden. Dag Langmyhr

Socket og ServerSocket

IN1010. Fra Python til Java. En introduksjon til programmeringsspråkenes verden Dag Langmyhr

Algoritmer og datastrukturer A.1 BitInputStream

INF1010. Grensesnittet Comparable<T>

INF1010 Sortering. Marit Nybakken 1. mars 2004

Tilstandsmaskiner med UML og Java

Intentor Helpdesk - Installasjon Step #3: Microsoft Reporting Services

Kanter, kanter, mange mangekanter

JavaServer Pages (JSP)

Mattespill Nybegynner Python PDF

Hittil har programmene kommunisert med omverden via tastatur og skjerm Ønskelig at data kan leve fra en kjøring til neste

IN1010. Fra Python til Java. En introduksjon til programmeringsspråkenes verden Dag Langmyhr

Bruk av NetBeans i JSP-delen av Web-applikasjoner med JSP og JSF

Avdeling for ingeniørutdanning Institutt for teknologi

Seminaroppgaver IN1010, uke 2

JDBC. Java DataBase Connectivity SQL i Java Læreboken: 8.5, s Forelesning i TDT4145, 9. mars 2004 Av Gisle Grimen

2. Beskrivelse av installasjon av SQL Server 2005 og hvordan lage databasen som trengs av administrasjonsprogrammet:

Introduksjon til objektorientert programmering

TDT4110 Informasjonsteknologi grunnkurs: Kapittel 7 Filer og unntak ( exceptions ) Professor Alf Inge Wang Stipendiat Lars Bungum

Klasser skal lages slik at de i minst mulig grad er avhengig av at klienten gjør bestemte ting STOL ALDRI PÅ KLIENTEN!

Transkript:

J2EE og distribuerte systemer Leksjon 9: Session Beans Leksjonen er forfatters eiendom. Som kursdeltaker kan du fritt bruke leksjonen til eget personlige bruk. Kursdeltakere som ønsker å bruke leksjonene f.eks. til undervisning eller kursformål må ta direkte kontakt med forfatter for nærmere avtale. Copyright: Tomas Holt/TISIP Publisert 28.01.03. Frist innlevering av øvingsoppgaver 07.02.03 Innhold 1 KOMMENTAR TIL LEKSJONEN... 2 2 DATASOURCE-OBJEKTER... 2 3 ANDRE DATABASESYSTEMER ENN CLOUDSCAPE... 3 4 NOEN ORD OM JNDI... 5 4.1 JNDI OG DATASOURCE-OBJEKTER... 5 4.2 JNDI OG MILJØVARIABLER... 7 5 SESSION BEANS MED TILSTANDER... 9 5.1 TILSTANDSLØSE SESSION BEANS... 9 5.2 FLERBRUKERPROBLEMATIKK OG SYNKRONISERING...10 5.3 SESSION BEANS MED TILSTANDER...10 5.4 EJBCREATE()...11 5.5 HVORDAN SER EN TFSB UT?...11 5.6 METODEN EJBPASSIVATE()...14 6 HVORDAN BEHANDLE UNNTAK...15 7 EKSEMPEL PÅ SESSION BEAN MED TILSTANDER...16 8 OPPGAVE / BRUK AV EN SESSION BEAN SOM CACHE...19 9 PROBLEMER?...24

1 Kommentar til leksjonen Vi har nå kommet ditt at du er i stand til å lage J2EE-applikasjoner. I forrige leksjon/øving brukte vi tilstandsløse Session Beans. Vi skal nå se på hva Session Beans med tilstander kan gjøre for oss, og hva som vil være forskjellen på disse i forhold til de tilstandsløse. I tillegg skal vi se på noen triks i forhold til JNDI, og ikke minst må vi se litt nærmere på hvordan vi behandler unntak (Exceptions) i en EJB. Leksjon 7 og 8 har innbefattet en god del teori. Dette er nødvendig for å skape forståelse. I denne uken blir det imidlertid mindre teori og mer praksis. Øvingen vil være litt større enn tidligere. Leksjonen omhandler kapittel 6 i boka. 2 DataSource-objekter Bruk av databaser blir viktig i fortsettelsen av dette kurset. I forbindelse med J2EE bruker man DataSource-objekter som grensesnitt mot en database, se figuren under. Et slikt DataSource-objekt vil være koblet mot en bestemt database og vite hvilken driver som skal brukes. Som programmerer vil du bruke JNDI for å finne et slikt DataSource-objekt og så kalle getconnection() på dette objektet. Objektet vil da returnere et Connection-objekt som kan brukes mot databasen (det er med andre ord opp til DataSource-objektet å lage selve oppkobligen mot databasen). Merk at dette DataSource-objeket kan holde styr på en pool av (dvs. flere) oppkoblinger mot databasen. Når en klient kaller getconnection() på objektet returneres et av de ledige Connection-objektene. Merk at det ikke er nødvendig å sette opp en ny oppkobling mot databasen i dette tilfellet, oppkoblingen er allerede laget og kan brukes direkte. Når klienten er ferdig med Connection-objektet så kalles metoden close() på objektet. Dette vil føre til at oppkoblingen returneres til DataSource-objektet som kan gi Connectionobjektet til en annen klient. Selve oppkoblingen (Connection) mot databasen vil derfor aldri bli lukket. Dette er en stor fordel fordi det er ressurskrevende å lage slik oppkoblinger mot databasen (du har sikkert sett at oppkobling mot en database tar lang tid. Dette kommer som oftest av at det tar lang tid å koble opp mot databasen. Selve spørringen tar ofte lite tid). I tillegg så er det en fordel at klientene deler på oppkoblingene mot databasen fordi man på denne måten unngår å ha en oppkobling pr. klient. Det er to grunner til at dette kan være uønsket. Lisenser for bruk av databasesystemet betales ofte etter hvor mange samtidige oppkoblinger man har mot systemet. Færre oppkoblinger gir billigere lisens. Mange oppkoblinger mot databasesystemet bruker ekstra ressurser, noe som kan gjøre databasesystemet tregere. Det går altså ressurser på å holde styr på mange oppkoblinger, i stedet for å bruke disse ressursene på å gjøre databaserelaterte oppgaver.

DataSource-objekt Connection-objekter Oppkoblinger Database... Klientkode: DataSource ds =...;//Bruk JNDI for å finne objektet Connection con = ds.getconnection();//låner et av Connection-objektene //Gjør databasekall... con.close(); //Gir Connection-objektet tilbake til DataSource-objektet 3 Andre databasesystemer enn cloudscape I utgangspunktet vil applikasjonstjeneren sørge for å opprette et DataSource-objekt for cloudscape. Du kan imidlertid sørge for at applikasjonstjeneren oppretter DataSourceobjekter for andre databasesystemer. F.eks. kan det være aktuelt å bruke en oracle database i stedet for cloudscape. For at dette skal gå må du først få tak i en databasedriver som støtter connection-pooling. Denne driveren må legges på katalogen <j2ee-home>\lib\system. Nå må du velge Tools > Server Configuration > Standard i deploytool.

Ved å velge Add for JDBC Drivers i skjermbildet over kan du legge til en ny driver-klasse. Den nederste av de to har jeg lagt til for å kunne bruke oracle. Når driveren for databasesystemet er lagt til kan du sørge for at det blir laget et DataSource-objekt for databasesystemet, og hvilket JNDI-navn objektet skal ha. Trykk Add for Datasources. I venstre del av tabellen må du angi JNDI-navn (valgfritt, men bør starte med jdbc/) og i høyre del må du angi JDBC URL. Denne vil være forskjellig for ulike databasesystemer. I figuren over kan du se at jeg har laget et DataSource-objekt med JNDI-navn jdbc/oracle. Tilslutt må du gå inn i filen <j2ee-home>\bin\userconfig.bat og endre denne slik at J2EE_CLASSPATH settes til å peke på jar-filen databasedriveren ligger i. Hos meg blir da filen som vist under. De endringene jeg har gjort er med uthevet skrift. REM REM Copyright 2002 Sun Microsystems, Inc. All rights reserved. REM SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. REM rem J2EE_CLASSPATH is appended to the classpath referenced by the EJB server. rem J2EE_CLASSPATH must include the location of the JDBC driver classes rem (except for the Cloudscape driver shipped with this release). rem Each directory is delimited by a semicolon. rem set J2EE_CLASSPATH=d:\j2ee\lib\system\ojdbc14.jar rem rem JAVA_HOME refers to the directory where the Java(tm) 2 SDK rem Standard Edition software is installed. rem rem set JAVA_HOME= rem Før du tar i bruk ditt nye DataSource-objekt må du restarte applikasjonstjeneren. Du vil ved oppstart av tjeneren se hvilke DataSource-objekter som blir opprettet (og hvilket navn de får).

4 Noen ord om JNDI Vi har tidligere vært inne på at JNDI kan brukes til mange forskjellige typer oppslag (DNS, filer osv.). Når vi bruker JNDI i forbindelse med J2EE-applikasjoner gjøres dette på en litt særegen måte. 4.1 JNDI og DataSource-objekter JNDI brukes for å finne DataSource-objekter. Når vi lager EJB er som skal koble opp mot en database vil vi altså gjøre en JNDI-lookup for å finne et DataSource-objekt. I forrige leksjon ble det beskrevet at for å unngå problemer når det gjelder JNDI-navn så får hver enkelt J2EEapplikasjon sin egen JNDI-kontekst. Når vi gjør en lookup etter en EJB gjør vi derfor noe slik: InitialContext kontekst = new InitialContext(); Object obj = kontekst.lookup( java:comp/env/ejb/helloworld ); HelloWorldHome home = (HelloWorldHome)PortableRemoteObject.narrow(obj,HelloWorldHome.class); Som forklart i forrige leksjon kan strengen java:comp/env/ejb/helloworld mappes til et globalt JNDI-navn (altså et navn som er unikt i denne navnetjeneren). Dette gjør at hver enkelt applikasjon kan ha ulike navn for samme EJB (noe som hindrer navnekonflikter). Det samme gjelder når vi skal bruke databaser. Når en EJB skal ha kontakt med en database så vil man ikke bruke det globale JNDI-navnet. Man bruker et navn som starter med java:comp/env/jdbc. La oss tenke oss at vi har en EJB som gjør databasekall mot tabellen Konto. Utvikleren av EJB en har valgt å gjøre følgende JNDI-oppslag for å finne databasen. InitialContext kontekst = new InitialContext(); DataSource ds = (DataSource)kontekst.lookup( java:comp/env/jdbc/kontodb );

Merk at i dette tilfellet så kan vi caste direkte. Du har nettopp kjøpt denne EJB en, men har ingen database på applikasjonstjeneren din som heter dette. Du har imidlertid cloudscape kjørende og du har opprettet tabellen Konto. Du ønsker følgelig å bruke den databasen som du allerede har. For å gjøre dette må du sørge for å mappe navnet java:comp/env/jdbc/kontodb til det globale JNDI-navnet jdbc/cloudscape (dette er det globale navnet for DataSourceobjektet til cloudscape når vi bruker j2ee-tjeneren). For å gjøre denne mappingen må vi bruke deploytool, se figur under. Først finner du EJB en som skal bruke DataSource-objektet. Trykk så på skillearket for Resource Refs. Skriv inn navnet som blir brukt i EJB en. Her blir det jdbc/kontodb (java:comp/env er implisitt gitt). Velg Type til å være DataSource og velg sharable for best ytelse (deler oppkoblinger med andre). Nå har du gitt beskjed om hvilket navn EJB en bruker. Du må nå sørge for å få mappet dette navnet til det globale JNDI-navnet for databasen i systemet (jdbc/cloudscape). I feltet for JNDI Name velger du jdbc/cloudscape. Nå vil EJB en bruke cloudscape som database. Dersom databasesystemet krever brukernavn og passord (cloudscape gjør det ikke) så må du også fylle inn feltene User Name og Password. Ved å trykke på jar-filen for komponenten (se figuren under) og velge skillearket for JNDI, kan du se hvilke JNDI-navn osv. som brukes for denne komponenten.

4.2 JNDI og miljøvariabler Vi kan også ha bruk for JNDI i våre EJB er i andre sammenhenger. Se på koden vist under. Her har jeg laget en generell klasse som implementerer SessionBean-interfacet. I forrige leksjon observerte vi at de fleste metodene i dette interfacet godt kan være tomme. Det er derfor greit å lage seg en klasse som vi kan arve fra i stedet for å ha alle metodene i hver EJB vi lager. Du kan se at hver metode skriver ut en streng som kan brukes for debugging. I dette tilfellet vil utskriften havne i konsollvinduet hvor applikasjonstjeneren kjører. Dette kan være svært nyttig når ting ikke går helt som planlagt, og merk mine ord, det vil du oppleve i fortsettelsen. Ok, se spesielt på metoden log(). Det er denne metoden som sørger for å skrive ut, men utskrift skjer kun hvis variabelen debug=true. Grunnen til dette er at vi kun vil ha utskrift når vi selv ønsker det (tro det eller ei men utskrift til konsollvindu er ganske ressurskrevende i Java). Spørsmålet er hvordan endrer vi variabelen debug uten å måtte rekompilere klassen? Jo, vi bruker en miljøvariabel som inneholder verdien vi ønsker. Vi finner denne miljøvariabelen ved å bruke JNDI. I en EJB vil bestandig metoden setsesisoncontext() være den metoden som blir kalt først. Vi finner derfor verdien til miljøvariabelen debug i denne metoden. Merk at vi også tar vare på en referanse til et SessionContext-objekt. Dette er faktisk en referanse til EJB-objektet, men vil ikke brukes så ofte i forbindelse med SessionBeans.

import javax.ejb.*; 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; Det vi trenger nå er en måte å angi verdien til debug på. Dette gjør vi ved å bruke deploytool, se figuren under. Velg den aktuelle EJB en og trykk på skillearket for Env. Entries. Trykk så Add og velg navnet på miljøvariabelen. Igjen er java:comp/env implisitt gitt. Velg hva slags type variabelen det er snakk om og verdien til variabelen. Når vi nå legger applikasjonen ut på web-tjeneren vil denne verdien være tilgjengelig for EJB en.

Hvis du vil teste bruken av miljøvariabler selv, kan du bruke spill.ear vedlagt leksjonen (se lenger ned for gjennomgang av denne applikasjonen). Her benyttes miljøvariabler og du kan teste hva som skjer når de endres. 5 Session Beans med tilstander La oss først ta et lite tilbakeblikk på tilstandsløse Session Beans. 5.1 Tilstandsløse Session Beans I forrige leksjon gikk vi igjennom hvordan en TLSB (tilstandsløs Session Bean) kunne lages. Som vi var inne på så vil aldri en TLSB kunne huske hva klienten har gjort tidligere. Hvorfor er det slik? La oss si at du lager en TLSB. Denne har forretningsmetodene økteller() og hentteller(). Metodene sørger hhv. for å øke en objektvariabel med 1 og å hente ut verdien i objektvariabelen. Du sørger for å legge denne ut på applikasjonstjeneren, som igjen sørger for å opprette 4 objekter av EJB-implementasjonenklassen. Det vil nå finnes 4 objekter som kan ta i mot forespørslene fra klientene. La oss si at vi har 1 klient i systemet vårt. Vi starter opp klienten vår. Klienten finner EJB-objektet til EJB en og kaller metoden økteller() 10 ganger. For hver gang hentes verdien på telleren ut med metoden hentteller(). Du forventer kanskje nå at verdien siste gang blir 10? Det viser seg imidlertid at verdien bare er 3. Hva har skjedd? Det har seg sånn at når vi bruker tilstandsløse Session Beans så vil klienten kunne betjenes av hvilket som helst av objektene opprettet av EJB-implementasjonsklassen. Dette fører til at klienten ikke nødvendigvis kommuniserer med samme EJB-implementasjonobjekt ved hvert kall. Figuren under viser hvordan to kall til økteller() kan betjenes av tjeneren. Riktig nok så vil hver enkelt EJB-implementasjonsobjekt kunne ha sin egen teller, men i.o.m at klienten

aldri vet akkurat hvilket objekt han kommuniserer med, så vil det være helt bortkastet at EJB en tar vare på informasjon på vegne av klienten. Container EJB-implementasjon 1: økteller() EJB-implementasjon 1: økteller() Klient 2: økteller() EJBobjekt EJB-implementasjon 2: økteller() EJB-implementasjon Pool av 4 tilstandsløse Session Beans Det at det aldri spiller noen rolle hvilket EJB-implementasjonsobjekt som betjener klienten gjør at TLSB kan tilby god ytelse. 5.2 Flerbrukerproblematikk og synkronisering Det virker kanskje rart at metodene i en EJB-implementasjonsklasse ikke er synkroniserte. Saken er at det er containeren sin oppgave å håndtere flerbrukerproblematikk. Dette gjøres ved at det aldri er mer enn en klient som betjenes av et EJB-implementasjonsobjekt. Følgelig vil det ikke være bruk for oss som programmerere å ta høyde for flerbrukerproblematikk. 5.3 Session Beans med tilstander. I det øyeblikket vi vil at EJB en faktisk skal huske informasjon (tilstander) som er knyttet til en bestemt klient, må vi velge å bruke TFSB (tilstandsfull Session Bean). I dette tilfellet vil hver enkelt klient ha sitt eget EJB-implementasjonsobjekt. Fordelen med en slik løsning er at EJB en er i stand til å huske på de kallene klienten har gjort tidligere. La oss se på eksemplet fra forrige kapittel på nytt. Klienten kaller økteller() 10 ganger, og kaller så hentteller(). I dette tilfellet vil det bestandig returneres tallet 10. Telleren er altså knyttet direkte til hvor mange ganger klienten kaller metoden.

Container EJB-implementasjon Klient 1: økteller() 2: økteller() EJBobjekt 1: økteller() 2: økteller() 1 Session Bean for hver klient Styrken til TFSB er også dens svakhet. Det at hver enkelt klient må ha sitt eget EJBimplementasjonsobjekt gjør at det fort kan bli veldig mange slik objekter. Har vi 500 klienter må vi også ha 500 EJB-implementajonsobjekter. Bruker vi TLSB kan det være at vi klarer oss med 10 EJB-implementasjonsobjekter. Tilstander er derfor ressurskrevende, og man bør vurdere om man trenger det. 5.4 ejbcreate() Av de to foregående kapitlene kan man utlede at å kalle create()-metoden på en tilstandsløs Session EJB (home-objektet), faktisk ikke nødvendigvis fører til opprettelsen av et nytt EJBimplementasjonsobjekt. Det er derfor i dette tilfellet ikke noen sammenheng mellom hvor mange ganger en klient kaller create() og hvor mange ganger metoden ejbcreate() faktisk blir utført (det opprettes nye implementasjonsobjekter kun når man trenger det). Når vi bruker TFSB blir saken en helt annen. I dette tilfellet skal hver enkelt klient ha sitt eget EJB-implementasjonsobjekt og følgelig vil ejbcreate() kalles for hver gang create() blir kalt på home-objektet. For TLSB vil aldri ejbcreate() ha noen argumenter. Når vi bruker Session Beans med tilstander kan vi ha flere ejbcreate() metoder, og selv bestemme hvilke argrumenter metoden skal ta. Create() metodene i Home-interfacet må også i dette tilfellet ha tilsvarende argumenter. 5.5 Hvordan ser en TFSB ut? Session Beans med og uten tilstander ser veldig like ut. Vi implementerer i begge tilfellene interfacet SessionBean. Faktisk så kan koden være akkurat lik i begge tilfellene (selv om dette nok vil være meningsløst). Vi angir om det er en TLSB eller TFSB i deploytool når vi lager jar-filen for EJB en. Skjermbildet under viser at du har valget mellom Stateless og Statefull som gir hhv. TLSB og TFSB.

Figur 1: TFSB eller TLSB? I forrige leksjon brukte vi EJB en HelloWorld. Denne hadde flere metoder som ikke gjorde noe. Disse var: ejbactivate() ejbpassivate() ejbremove() setsessioncontext() Som forklart skal ikke verken ejbactivate() eller ejbpassivate() ha noen implementasjon i en tilstandsløs Session Bean. Grunnen er at disse metodene aldri vil bli kalt! Når vi snakker om en Session Bean med tilstander blir det hele litt annerledes. I dette tilfellet kan ma n komme i den situasjonen at det ikke lenger er nok minne i maskinen til å holde alle EJB ene i minnet (husk at vi ikke har noen kontroll over hvor mange objekter som blir opprettet, det er klientene som bestemmer dette). Hvis man skulle komme i en situasjon der man ikke lenger kan holde EJB-implementasjonsobjektet i minnet, må man sørge for å lagre det på harddisken. Dette er imidlertid ikke bestandig en rett fram oppgave. Serialiserbare objekter kan enkelt lagres på denne måten, men hva om man har referanser til ikkeserialiserbare objekter? Disse kan helt klart ikke lagres på harddisken, og det er derfor nødvendig å bestemme hva som skal gjøres hvis man kommer i en slik situasjon.

Hvis applikasjonstjeneren finner ut at den må lagre et EJB-implementasjonsobjekt på disk så vil den sørge for å kalle metoden ejbpassivate() før lagringen finner sted. Dette gjør at du som programmerer får sjansen til å fjerne referanser til ikke-serialiserbare objekter. Når objektet hentes fra disk vil ejbactivate() kalles slik at man kan sette opp nye referanser til de ikke serialiserbare objektene. Merk at det er viktig at alle referanser til ikke-serialiserbare objekter settes til null i ejbpassivate(). Hva slags objekter er ikke serialiserbare? For å ta et realistisk eksempel så kan vi nevne database Connection-objekter. Koden under viser en Session Bean med tilstander. Du kan se at vi har referansen forbindelse. Dette er en oppkobling mot en database. Man har laget EJB en slik at det blir satt opp en forbindelse mot databasen når EJB en blir opprettet. Dette gjør at man ikke trenger å sette opp en ny forbindelse mot databasen ved hvert kall til gjørnoemeddatabase(). Dette vil igjen sørge for at denne metoden utføres raskere. Problemet oppstår hvis EJB en må midlertidig lagres til disk. Da må vi sørge for at alt som ikke kan serialiseres er satt til null. Forbindelse settes til null i ejbpassivate(). En ny oppkobling blir laget i det metoden ejbactivate() blir kalt. import javax.ejb.*; import java.sql.*; public class EksempelBean implements GeneriskSessionBean{ Connection forbindelse = null; //holder oppkobling mot DB slik at vi ikke trenger langsom oppkobling //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 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(...); Merk at vi normalt ikke vil lage databaseoppkoblinger slik det er gjort i koden over! Vi ønsker vanligvis heller å bruke et DataSource-objekt da dette gir bedre effektivitet. DataSource-objekter er vanligvis er serialiserbare. Vi trenger derfor ikke å gjøre noe i ejbpassivate() og ejbactivate(). Ved bruk av et DataSource-objekt kan koden bli slik: import javax.ejb.*; import java.sql.*; import javax.naming.*; public class EksempelBean2 implements GeneriskSessionBean{ DataSource ds = null; public void ejbcreate() throws CreateException{ InitialContext init = InitialContext(); ds = (DataSource)init.lookup("java:comp/env/jdbc/DataSource"); catch(exception e){ //unntaksbehandling public void gjørnoemeddatabase(){ Connection forbindelse = ds.getconnection();//henter fra pool Statement setning = forbindelse.createstatement(); //gjør spørring mot database catch(exception e){ //unntaksbehandling finally{ if (setning!= null) setning.close(); if (forbindelse!= null) forbindelse.close();//tilbake til pool catch(exception e){ //trenger ikke gjøre noe i metodene ejbpassivate()/ejbactivate() //bruker derfor de arvede metodene fra GeneriskSessionBean 5.6 Metoden ejbpassivate() Som forklart over trenger vi ikke å passivisere DataSource-objekter da disse kan serialiseres. Det samme gjelder faktisk referanser til home-objekter og EJB-objekter. Dvs. at du godt kan ha referanser til andre EJB er uten at du må ta spesielle hensyn til dette.

6 Hvordan behandle unntak Det er to typer unntak vi må være klar over i forbindelse med J2EE. Dette er applikasjonsunntak (application exception) og systemunntak (system exception). Applikasjonsunntak er en type unntak som vi kan ta høyde for som programmerer. Dette er unntak som ikke er fatale. For eksempel kan det være at brukeren skriver inn ulovlige data. I dette tilfellet kan dette føre til at det kastes et unntak (for eksempel BadInputException). Dette er et unntak som klienten kan ta imot (catch) og sørge for å gi brukeren mulighet til å taste inn opplysningene på nytt. Systemunntak på sin side indikerer unntak som vi ikke kan komme oss ut av. For eksempel hva gjør man hvis databasen plutselig forsvinner? Hvis du har en oppkobling mot databasen vil du få et unntak, men poenget er at du ikke kan gjøre noe med problemet. For at dette problemet skal løses så må systemadministrator sørge for å få databasen opp igjen. I dette tilfellet er det beste man kan gjøre som programmerer bare å gi en fornuftig feilmelding til brukeren. Dette kan for eksempel være: Databasen systemet bruker er dessverre ikke tilgjengelig, vennligst send en e-post til... Applikasjonsunntak behandles som du er vant med. Hvis du selv skal lage slike unntak så arver du fra klassen Exception og sørger for at det i metodehodet er beskrevet at dette unntaket kan hives. Tenk deg at vi har en biblioteksapplikasjon. La oss si at du vil se alle bøker i en spesiell kategori. Metodehodet i EJB-implementasjonenklassen kan da være slik: public ArrayList getkategori(string kategori) Hva skal skje hvis klienten oppgir en kategori som ikke finnes? Er dette i tilfellet et unntak? Hvis brukeren selv skriver inn kategorien som er ønsket, er sannsynligheten stor for at det faktisk er en skrivefeil som gjør at gal kategori blir forespurt. Tenker man slik, kan det være naturlig å hive et unntak tilbake til klienten som spesifiserer at kategorien faktisk ikke finnes. Det andre alternativet er selvsagt bare å returnere en tom ArrayList og la klienten finne ut av dette selv. La oss si at vi velger metoden med å hive et unntak. Vi lager oss en unntaksklassen FeilKagetoriException som arver fra Exception. Vi endrer så metodehodet slik: public ArrayList getkategori(string kategori) throws FeilKategoriException Metodehodet beskriver nå at det fra denne metoden kan komme et applikasjonsunntak, som klienten bør være i stand til å behandle. Merk at vi også må sørge for at beskrivelsen av metoden i Remote-interfacet til metoden endres til å hive unntaket. Systemunntak derimot tar vi imot inne i EJB en. Disse kan hives videre som en EJBException(). Her er et kort eksempel på en metode som ligger i en EJB: 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);

Som du ser av koden så sørger vi for å putte all databasekommunikasjonen i en try/catchblokk. Hvis det skulle oppstå problemer skriver vi ut en beskrivelse. Denne utskriften er kun ment som logging på tjeneren slik at vi kan se hva som har skjedd. Klienten vil ikke se noe av denne utskriften. Nå sørger vi for å hive unntaket videre som en EJBException. Hva dette fører til er imidlertid situasjonsavhengig. Det er nå opp til applikasjonstjeneren å vurdere unntaket. Kanskje er det et unntak som applikasjonstjeneren kan gjøre noe med selv. I dette tilfellet blir det ikke sent feilmelding til klienten. Hvis applikasjonstjeneren ikke selv kan gjøre noe med unntaket vil det bli sent videre til klienten som en RemoteException (husk at alle fjernmetoder kan kaste dette unntaket). I de fleste tilfeller vil det nok være fornuftig at EJB ene som klienten kommuniserer med hiver spesifiserte unntak i stedet for EJBException(). Klientene har mer bruk for en enkel forklaring på hva problemet er, enn en kryptisk feilmelding. Du må derfor selv vurdere når de det er fornuftig å lage egne unntaksklasser og når du vil bruke EJBException. Eksempler på ulike måter å gjøre dette på kommer etter hvert. Er du i tvil kan du bruke EJBException. 7 Eksempel på Session Bean med tilstander La oss se et eksempel på en TFSB. Vi skal lage et enkelt spill. Poenget er at spillet skal generere et tilfeldig tall. Klienten skal så få et bestemt antall forsøk til å tippe riktig tall. For hver gang klienten tipper et tall så vil EJB en gi beskjed om tallet er større eller mindre enn tippet. Hele poenget her er at EJB en må ha tilstand. For det første må EJB en holde styr på hvor mange ganger klienten har tippet, og for det andre må den også vite hvilket tilfeldig tall som ble generert i utgangspunktet. La oss først se på Home-interfacet: import javax.ejb.*; import java.rmi.*; public interface GjettTallSpillHome extends EJBHome{ GjettTallSpill create() throws CreateException, RemoteException; GjettTallSpill create(int antallforsok) throws CreateException, RemoteException; Som du kan se har vi i dette tilfellet to create()-metoder. Dette kan vi gjøre fordi dette er en TFSB. Vi får tilsvarende ejbcreate()-metoder i implementasjonsklassen. Bruker vi create()- metoden uten argument vil vi bruke et forutbestemt antall forsøk på å tippe tallet. Bruker vi den andre create()-metoden kan klienten selv bestemme hvor mange forsøk man skal ha. Remote-interfacet blir slik: import javax.ejb.*; import java.rmi.*; public interface GjettTallSpill extends EJBObject{ int gjett(int tall) throws RemoteException; boolean fleresjanser() throws RemoteException;

Her har vi to metoder som utgjør selve forretningslogikken. En metode for å tippe et tall. Denne metoden vil returnere et negativt tall om tallet er mindre enn tippet, positivt hvis tallet er større enn tippet og null hvis de er like. Metoden fleresjanser() gir klienten mulighet til å finne ut om han har flere sjanser til å tippe. EJB-implementasjonsklassen blir slik: import javax.ejb.*; import javax.naming.*; //JNDI import javax.rmi.*; //PortableRemoteObject import java.util.*; public class GjettTallSpillBean extends GeneriskSessionBean{ int antallforsok; int riktigtall; int storstemuligetall = 50; public void ejbcreate() throws CreateException{ setklassenavn("gjetttallspillbean");//for debug output log("ejbcreate()"); riktigtall = lagtilfeldigtall(); //henter antall forsøk fra en miljøvariabel med JNDI-oppslag InitialContext init = new InitialContext(); Object obj = init.lookup("java:comp/env/antallforsok"); Integer tmpint = (Integer)PortableRemoteObject.narrow(obj, Integer.class); antallforsok = tmpint.intvalue(); catch(exception e){ log("ejbcreate() " + e); throw new CreateException("Problemer med å lage EJB'en " + e); //klienten oppgir antall forsøk.. public void ejbcreate(int startantallforsok) throws CreateException{ setklassenavn("gjetttallspillbean");//for debug output log("ejbcreate()"); antallforsok = startantallforsok; riktigtall = lagtilfeldigtall(); catch(illegalargumentexception e){ log("ejbcreate(int) " + e); throw new CreateException("Feil med tall. Oppgi en gyldig int"); //returnerer 0 hvis riktg, større enn 0 hvis tallet er større en tippet dvs. det må tippes et større tall //mindre enn 0 hvis det må tippes et mindre tall //merk at vi ikke legger oss bort i presentasjonen her, det blir opp til klienten!!! public int gjett(int tall){ antallforsok--; //har brukt nok et forsøk return riktigtall - tall; public boolean fleresjanser(){ if (antallforsok > 0) return true; else return false; //returnerer et tall mellom 1 og storstemuligetall

private int lagtilfeldigtall(){ Random random = new Random(); return random.nextint(storstemuligetall) + 1; Først legg merke til at klassen arver fra GeneriskSessionBean, noe som gjør at vi slipper å implementere alle metodene i Session-interfacet. Deretter kan du merke deg teksten i uthevet skrift. Her bruker vi en miljøvariabel til å bestemme hvor mange forsøk klienten skal ha til å tippe. Vi må altså sørge for å sette verdien til denne i deploytool som vist under. Resten av koden bør være ganske selvforklarende. Jeg vil imidlertid nevne at man ikke trenger å kalle lagtilfeldigtall() i begge ejbcreate()-metodene. Vi er selvsagt avhengig av å lage et tilfeldig tall ved opprettelsen av et EJB-implementasjonsobjekt, men dette kan like godt ligge i setsessioncontext()-metoden (arves fra GenersikSessionBean). Denne metoden utføres faktisk før ejbcreate()-metoden og gjør derfor samme nytten. Fordelen er at den utføres uansett hvilken ejbcreate()-metode som blir kalt. Ok, da er det bare klienten igjen.

import javax.rmi.*; import javax.naming.*; import java.io.*; public class GjettTallKlient{ public static void main(string args[]){ BufferedReader les = new BufferedReader(new InputStreamReader(System.in)); InitialContext init = new InitialContext(); Object obj = init.lookup("java:comp/env/ejb/gjetttallspill"); GjettTallSpillHome home = (GjettTallSpillHome)PortableRemoteObject.narrow(obj, GjettTallSpillHome.class); GjettTallSpill spill = home.create(); boolean riktiggjettet = false; do{ //går i løkke til brukeren har gjettet riktig eller brukt opp sine forsøk System.out.print("Tipp et tall mellom 1 og 50: "); String strtall = les.readline(); int tall = Integer.parseInt(strTall); int svar = spill.gjett(tall); if (svar > 0){ System.out.println("Tallet er større enn " + tall); else if (svar < 0){ System.out.println("Tallet er mindre enn " + tall); else{ riktiggjettet = true; catch(numberformatexception e){ System.out.println("Skjerpings. Tallet er ikke gyldig!"); while (spill.fleresjanser() &&!riktiggjettet); // har fortsatt flere sjanser igjen if (riktiggjettet){ System.out.println("Gratulerer du har gjettet riktig"); else{ System.out.println("Sorry. Du har brukt opp dine sjanser"); spill.remove();//frigjør ressurser på tjeneren!! catch(exception e){ System.out.println("Feil i klienten: " + e); Koden er vedlagt eksempelfilene til leksjonen. Ear-filen til applikasjonen er også lagt ved slik at du kan åpne denne direkte i deploytool. Kjør eksemplet. Prøv også å forandre miljøvariabelen for antall forsøk. Du må legge ut applikasjonen på nytt (deploy) for at virkningen skal tre i kraft. 8 Oppgave / bruk av en Session Bean som cache Tenk deg at du har en database som inneholder mange bøker. Hver bok har et isbn-nummer (unikt for boka), tittel og pris. Det er ønske om å lage en Session Bean som henter ut bøkene fra databasen og returner bøkene i en ArrayList. Klienten kan så bruke denne listen til å vise fram bøkene til brukeren.

Vi har flere mulige løsninger her. Den første og kanskje mest nærliggende løsningen er å lage en TLSB som ser noe slik ut (dette er ikke fullstendig kode): public class BokOversikt extends GeneriskSessionBean{ private ArrayList bøker = null; public void ejbcreate() throws CreateException{ //finn database og opprett oppkobling ResultSet res = //spørring som henter alle bøker (select * from bok) while (res.next()){ String isbn = res.getstring("isbn"); String tittel = res.getstring("tittel"); int pris = res.getint(pris); Bok bok = new Bok(isbn,tittel,pris); //oppretter et hjelpeobjekt til å holde verdiene til en bok bøker.add(bok); //legger til boken i listen over alle bøkene catch(...){ //unntakshåndtering //metode som returnerer alle bøkene. public ArrayList hentbøker(){ return bøker; //evt. flere metoder... Merk at bøkene blir hentet ut fra databasen kun en gang (i ejbcreate()). Det gir effektivitet, i motsetning til å hente ut bøkene hver gang en klient ønsker å se bøkene. EJB en vil derfor i dette tilfellet fungere som en cache. Klassen Bok som brukes i koden over er en hjelpeklasse for å holde på verdiene til en bok. Den vil da typisk se noe slik ut: public class Bok{ private String isbn; private String tittel; private int pris; public Bok(String isbn, String tittel, int pris){ this.isbn = isbn; this.tittel = tittel; this.pris = pris; public String hentisbn(){ return isbn; public String henttittel(){ return tittel; public int hentpris(){

return pris; Klienten vil altså få en ArrayList fylt med Bok-objekter ved å kalle hentbøker() på EJB en. Klassen Bok er altså bare en hjelpeklasse for å samle alle verdiene til en bok. I forbindelse med J2EE vil man ofte kalle et slik hjelpeobjekt for et verdiobjekt (value object). Man vil derfor ofte gi et navn som gjenspeiler dette, f.eks. BokVO (nå er det ikke mulig å mistolke dette som et Remote-interface). Klienten kan nå vise fram alle bøkene til brukeren. Hva er så problemet med denne løsningen? Hva om det er snakk om 1000 bøker eller kanskje 1 million bøker? Det tar tid å overføre så mange bøker over et nettverk. Ingen er vel heller interessert i å bla igjennom så mange bøker. Det vi trenger er en måte å begrense hvor mange bøker klienten får. La oss si at vi gir klienten 10 og 10 bøker som den kan vise fram. Denne løsningen er helt klart mer fornuftig i dette tilfellet. Problemet er at nå må EJB en hele tiden holde styr på hvilke bøker i boklista som skal returneres neste gang. Dette må gjøres for hver enkelt klient. Nå har du jo allerede lært hvordan du skal lage en TFSB, så du klarer å løse også dette problemet. Før du går i gang og lager løsningen din er det imidlertid en ting du må huske på. Hver klient vil ha sitt eget EJB-implementasjonsobjekt. Dvs. at om du skulle lage en TFSB som ser noe slik ut public class BokOversikt extends GeneriskSessionBean{ private ArrayList bøker = null; private int hvorerjegilista; public void ejbcreate() throws CreateException{ //finn database og opprett oppkobling ResultSet res = //spørring som henter alle bøker (select * from bok) while (res.next()){ String isbn = res.getstring("isbn"); String tittel = res.getstring("tittel"); int pris = res.getint(pris); Bok bok = new Bok(isbn,tittel,pris); //oppretter et hjelpeobjekt til å holde verdiene til en bok bøker.add(bok); //legger til boken i listen over alle bøkene catch(...){ //unntakshåndtering //metode som returnerer neste 10 bøker. public ArrayList hentnestebøker(){ //lag en ny ArrayList med de 10 neste bøkene (hentes fra lista bøker) //oppdatere hvorerjegilista slik at vi kan hente ut riktige bøker neste gang så vil du ha like mange EJB-implementasjonsobjekter av klassen over som du har klienter! La oss si at du har 1000 klienter og at det er 10000 bøker. Det gjør at det nå i systemet må lagres

10 millioner Bok-objekter! Problemet her er selvsagt at alle EJB ene inneholder en fullstendig liste over alle bøkene. Løsningen på problemet er rett og slett å bruke flere EJB er i kombinasjon. Vi bruker en TLSB som inneholder lista med bøker. I denne TLSB en har vi en metode som heter hentbøker(int fraindeks, int tilindeks) Metoden vil returnere en ArrayList med det aktuelle utvalget av bøker. I tillegg så lager vi en TFSB som hele tiden holder styr på hvor klienten befinner seg i lista. Denne må da også ha en metode som klienten kan kalle, f.eks. hentnestebøker(). Denne må da sørge for å kalle metoden hentbøker() på TLSB en med riktig parametere (fraindeks og tilindeks). Klienten vil altså hele tiden forholde seg til sin egen TFSB, som igjen forholder seg til TLSB en. Vedlagt eksempelfilene til denne leksjonen finner du også nødvendige filer for TLSB en. Klienten som er brukt i løsningsforslaget er også lagt ved (du kan selvsagt lage din egen om du vil). Oppgaven din blir å lage TFSB en SideIterator som knytter det hele sammen. Du må selv sørge for å lage en J2EE-applikasjon som tester det hele ut. Ear-fil for applikasjonen og java-filer skal leveres. Tips: Du er avhengig av at EJB en din bruker min TLSB som heter CacheBean. For å få til dette må EJB en din gjøre en JNDI-lookup() for å finne min. Dette kan f.eks. bli noe slik: InitialContext init = new InitialContext(); CacheLocalHome home = (CacheLocalHome)init.lookup( java:comp/env/ejb/bokcache ); Dette blir altså som vanlig. Du kan se at jeg ikke har PortableRemoteObject.narrow() til å caste referansen. Dette er et bevist valg fordi jeg har valgt å lage lokale-interface i dette tilfellet. Du må så sørge for å opprette mapping for java:comp/env/ejb/bokcache til riktig EJB (se figuren under). I deploytool velger jeg da EJB en SideIteratorBean og velger skillearket EJB Refs. Her velger jeg Coded Name brukt for å finne den andre EJB en, hvilken Type EJB jeg forventer å finne osv. Vær obs på at du under Interfaces må velge Local (bruker lokaleinterface), og så må du oppgi navnene på de lokale interfacene (her CacheLocalHome og CacheLocal). Når vi bruker lokale interface velger vi ikke JNDI-navn, men velger riktig klasse direkte fra listen i Enterprise Bean Name.

Databasen som brukes i eksemplet finner du i den vedlagte filen bok.sql. Lag denne databasen i cloudscape ved å utføre bok.sql. Du må også sørge for at mapping mellom JNDI-navnet CacheBean bruker på databasen og jdbc/cloudscape. Filene som allerede er laget er: CacheBean BokVO (hjelpeobjekt som holder verdiene på en bok). BokKlient (klienten til applikasjonen). Du må altså lage filene: SideIteratorBean SideIterator (Remote-interface) SideIteratorHome (Home-interface) CacheLocal (lokalt interface) CacheLocalHome (lokalt home-interface) Du kan godt lage to Jar-filer i applikasjonen din. En for hver EJB. (Du kan også lage bare en). Husk at CacheBean bruker BokVO. Legg derfor til BokVO sammen med de andre klassene/interfacene når du oppretter JAR-filen for CacheBean. Se også figuren under.

9 Problemer? Det er fort gjort å komme opp i problemer når du lager J2EE-applikasjoner. Hvis du får feil ved utlegging av applikasjonen sørg først for å sjekke Tools > Verifier som forklart i forrige leksjon. Hvis at dette verktøyet fortsetter å plage deg med feilmeldinger etter at du selv mener du har rettet dem opp problemet, kan du prøve å trykke Update og så Delpoy for å se om dette gjør ting bedre. Hjelper ikke dette kan det være nødvendig å restarte applikasjonstjeneren (plagsomt men sant). Du kan også komme opp i situasjoner der du ikke får lagt ut applikasjonen din på applikasjonstjeneren, men Verifier ikke rapporterer noen feil. I dette tilfellet kan du ty til konsollvinduet til applikasjonstjeneren. Her vil det vises evt. feilmeldinger under kompilering av stub-klasser osv. Hvis ikke noe av det over hjelper, kan du prøve å lukke deploytool, applikasjonstjeneren og skrive cleanup på kommandolinjen. Da vil alle gamle filer i applikasjonstjeneren bli slettet, og du kan legge ut en fersk applikasjon (uten at noen av de gamle filene henger igjen). Siste utvei er rett og slett å starte maskinen på nytt.