4.1 Sessioner 71
4.1.1 Eksempel: En ønskeseddel 71
4.1.2 Sessioner er individuelle 72
4.1.3 At kassere en session 72
4.1.4 Avanceret: URL Rewriting 72
4.2 Eksempel: Login med adgangskode 73
4.2.1 Inkludering af kodefragmenter 75
4.3 Omdirigering 77
4.3.1 Klient-omdirigering (response.sendRedirect()) 77
4.3.2 Server-omdirigering (<jsp:forward />) 78
4.4 Appendiks: Almindelige JSP-koder 79
4.5 Appendiks: Implicit definerede objekter 80
4.5.1 request - anmodningen fra klienten 80
4.5.2 response - svaret til klienten 81
4.5.3 out - skrive tekst til klienten 81
4.5.4 session - objekt der følger den enkelte bruger 82
4.5.5 application - fælles for hele webapplikationen 83
4.5.6 config - den enkelte websides konfiguration 84
4.5.7 page - selve JSP-siden 85
4.5.8 exception - undtagelse opstået under kørsel 85
4.5.9 pageContext - alle objekterne samlet i ét 85
4.6 Opgaver 86
4.7 Test dig selv 86
4.8 Resumé 86
4.9 Avanceret: Fejlfinding i JSP 87
4.9.1 Del og hersk 87
4.9.2 Tjek om blokparenteser er balancerede 87
4.9.3 Kigge på den oversatte servlet 87
4.9.4 Kigge i log-filerne 87
4.9.5 Forstå staksporet 88
4.9.6 Hvis klasse(bibliotek)er ikke kan findes 88
En overfladisk forståelse af emnerne i dette kapitel forudsættes i det meste af bogen. Kapitlet forudsætter kapitel 3, Interaktive sider.
Hver bruger får tildelt et session-objekt, når de besøger en JSP-side. Sessionen følger brugeren, lige meget hvilken side han/hun er inde på og er derfor nyttig til at huske data, der skal følge brugeren.
Med den kan man gemme og hente oplysninger i løbet af en brugers besøg. Det kunne f.eks. være om brugeren har logget ind, et bruger-ID eller nogle oplysninger om hvilke valg, brugeren har foretaget. I en e-handels-applikation ville sessionsobjektet også være det helt rigtige at bruge til at huske varerne i brugerens indkøbskurv.
De vigtigste metoder i session-objektet er beskrevet i afsnit 4.5.4.
Det følgende eksempel lader brugeren indtaste nogle ønsker. Ønskerne huskes i en liste (af type ArrayList), der gemmes i sessionsobjektet (under navnet 'ønsker').
<%@ page language="java" import="java.util.*" %> <html> <head><title>Ønskeseddel</title></head> <body> Dette eksempel demonstrerer, hvordan session-objektet kan bringes til at huske brugerens indtastninger. <h3>Skriv et ønske</h3> Skriv noget, du ønsker. <form> <input type="text" name="oenske"> </form> <% // hent listen over ønsker ArrayList ønsker = (ArrayList) session.getAttribute("ønsker"); if (ønsker == null) { // hvis listen ikke findes: ønsker = new ArrayList(); // opret den session.setAttribute("ønsker", ønsker); // og registrer den under "ønsker" } // se om der kommer en parameter med endnu et ønske String ønske = request.getParameter("oenske"); if (ønske != null) { ønsker.add(ønske); // tilføj ønske til listen } if (ønsker.size()>0) { // udskriv ønsker i listen %> <h3>Ønskeseddel</h3> Indtil nu har du følgende ønsker:<br> <% // udskriv hele listen for (int i=0; i<ønsker.size(); i++) { %> Ønske nr. <%= i %>: <%= ønsker.get(i) %><br> <% } } %> </body> </html>
Forestil dig, at 20 personer samtidigt klikker rundt på det samme websted - f.eks. en e-butik. Oplysningerne om, hvad deres indkøbskurve indeholder, kan med fordel gemmes i sessionsobjektet.
For at kunne identificere brugerne i forhold til hinanden, sådan at to brugere ikke ved et uheld får byttet session (og dermed varer i indkøbskurven!) bruger serveren et unikt ID, som gemmes i en HTTP cookie (en lille tekstfil på brugerens PC, se afsnit 3.6.5). Brug af sessioner kræver derfor normalt, at brugeren har slået understøttelse af cookier til i sin netlæser (ellers må man ty til 'URL Rewriting' beskrevet i afsnit 4.1.4).
Vil du smide sessionen helt væk, kan det gøres med kaldet
session.invalidate()
Herefter er sessionen og alle dens data væk.
Hvis den pågældende bruger besøger en side igen, vil hun blive tildelt en ny, tom session.
I det foregående eksempel skulle alle beskyttede sider have et ovenstående tjek for, om brugeren er logget korrekt ind.
Det kunne derfor nok betale sig at have denne stump kode i en separat fil:
logintjek.jsp
<%
// Filnavn: logintjek.jsp
// Se om attributten "logget ind" er sat i sessionen
if (session.getAttribute("logget ind") == null) {
// brugeren er ikke logget ind, så send ham tilbage til login-siden
response.sendRedirect("login1.html");
}
%>
Herefter kan siderne blot inkludere fragmentet logintjek.jsp for at få tjekket om brugeren er logget ind:
login4.jsp
<%@ include file="logintjek.jsp" %>
<html>
<head><title>login4</title></head>
<body>
<h1>En anden beskyttet side</h1>
Denne tekst kan du kun se, hvis du er logget korrekt på.
</body>
</html>
Ovenstående eksempel viser også, hvordan man omdirigerer brugeren til en anden side:
response.sendRedirect("login1.html");
Brugeren vil da få siden login1.html i stedet for den side han egentlig spurgte om.
Omdirigering forudsætter, at der endnu ikke er sendt nogen data til brugeren. For JSP-sider sendes data normalt først, når afviklingen af siden er afsluttet (output er buffered), så en beslutning om omdirigering bør ske tidligt1 på siden.
Omdirigering kan faktisk gøres på to måder for JSP-sider: med response.sendRedirect():
<% response.sendRedirect("login1.html") %>
eller med JSP-koden <jsp:forward />:
<jsp:forward page="login1.html"/>
Forskellen mellem de to måder er måden, som omdirigeringen til den nye side foregår på: I det første tilfælde er klienten involveret i omdirigeringen, i det andet tilfælde sker omdirigeringen internt på serveren, uden at klienten ved det.
Modsat hvad man måske umiddelbart skulle tro, bliver al javakoden efter (d.v.s. neden under) en omdirigering faktisk udført, selvom outputtet fra siden skal kasseres2.
Med response.sendRedirect() sendes et speciel svar til netlæseren om, at den side, den lige har spurgt på, midlertidigt er flyttet et andet sted hen, hvorefter netlæseren vil spørge en gang til på den nye adresse.
Teknisk set sker der det, at serveren udnytter HTTP-protokollen (beskrevet i afsnit 3.7) til at omdirigere klienten. Når klienten spørger på login3.jsp med:
GET /JSP/kode/kapitel_04/login3.jsp HTTP/1.1
Host: javabog.dk:8080
vil serveren, i stedet for at kvittere med et '200 OK' og sende data som normalt, svare med et '302 Moved Temporarily' og fortælle klienten at den 'nye adresse' er login1.html:
HTTP/1.1 302 Moved Temporarily Location: http://javabog.dk:8080/JSP/kode/kapitel_04/login1.html
Netlæseren reagerer på '302 Moved Temporarily' ved omgående at sende en ny anmodning, der spørger på den nye adresse:
GET /JSP/kode/kapitel_04/login1.html HTTP/1.1
Host: javabog.dk:8080
Herefter forløber HTTP-kommunikationen som den plejer.
Netlæseren har været involveret i omdirigeringen og viser den nye URL i adresselinjen. Parametrene til den oprindelige anmodning sendes ikke igen sammen med den nye anmodning, de mistes.
JavaScript kan også bruges til at omdirigering. Følgende omdirigerer også til login1.html:
<html> <head><title>Omdirigering med JavaScript</title></head> <body> <script>document.location="/JSP/kode/kapitel_04/login1.html"</script> </body> </html>
JavaScript udføres i brugerens netlæseren og kan ikke bruges til sikkerhedsforanstaltninger, j.v.f. afsnit 8.5, Sikkerhed i en webapplikation (det kan f.eks. deaktiveres af brugeren).
Med <jsp:forward /> omdirigeres anmodningen internt i serveren. Det vil sige at den oprindelige anmodning klienten foretog (med de samme parametre etc.), besvares af en anden side i serveren.
Svaret sendes derefter tilbage til klientens netlæser, der ikke ved, at der skete en omdirigering. Den viser derfor den oprindelige URL i adresselinjen, ikke den nye adresse.
F.eks. kunne det være at klienten skulle omdirigeres til login-siden:
<jsp:forward page="login2.jsp"/>
Denne omdirigeringsmetode tillader at sende parametre videre til modtagersiden, da det samme request-objekt genbruges.
Man kan endda også tilføje parametre. Hvis brugeren eksempelvis kom ind på en side hvor der i JSP-siden, serveren udførte, stod:
<jsp:forward page="login2.jsp"> <jsp:param name="brugernavn" value="Jacob"/> <jsp:param name="adgangskode" value="hemli"/> </jsp:forward>
ville han blive automatisk logget ind (i øvrigt nok ikke videre hensigtsmæssigt).
Fra en servlet, eller fra Java-koden i en JSP-side kunne man skrive:
request.getRequestDispatcher("login2.jsp").forward(request,response);
Her er en oversigt over almindelige JSP-koder, som kan være nyttig til senere opslag.
JSP-kode |
Betydning |
---|---|
<% kode %> |
Javakode der udføres på serveren hver gang siden hentes. |
<%= udtryk %> |
Et udtryk. Værdien af udtrykket vil blive beregnet og indsat i stedet for koden. Eksempel (se også afsnit 2.2.1 Indlejrede java-udtryk): Syv gange 2 er <%= 7*2 %> |
<%-- kommentar --%> |
En kommentar. Vil ikke blive udført af serveren og vil aldrig blive sendt til klienten. Se også afsnit 2.5 Kommentarer. |
<%! erklæring %> |
Variabel- og metodeerklæringer. Disse oprettes/defineres én gang når siden indlæses (se afsnit 2.8.2). Bruges sjældent. |
<%@ page ... %> |
Side-direktivet, der indeholder information om
siden, f.eks.: |
<%@ taglib ... %> |
Et JSP-kodebibliotek (eng.: Tag library). En kraftfuld måde at definere og anvende foruddefinerere JSP-funktioner på (se afsnit 6). |
<%@ include ... %> |
Inklusions-direktivet. Inkluderer en fil (som om dens indhold blev klistret ind her - se afsnit 4.2.1 Inkludering af kodefragmenter). Eksempel <%@ include file="hej.jsp" %>
|
<jsp:include ... /> |
Kald en anden fil på køretidspunktet (som om der blev lavet en forespørgsel på den) og inkluderer dens uddata her, f.eks.: <jsp:include page="hej.jsp"/> |
<jsp:forward ... /> |
Omdirigerer til en anden side (som bliver kaldt i stedet, se afsnit 4.3.2): <jsp:forward page="hej.jsp"/> |
<jsp:plugin ... />
|
Genererer HTML-kode til en plugin. Bruges
sjældent. <jsp:plugin type="applet" code="MinApplet.class" |
<jsp:...>-koderne anvender XML-syntaks: Koder der begynder med <jsp: skal afsluttes med et /> eller også skal der være en tilsvarende slutkode. Det betyder at f.eks.:
<jsp:forward page="login1.html">
ikke må stå alene, men skal afsluttes af den tilsvarende slut-kode:
</jsp:forward>
For at slippe for at skrive en masse slut-koder i de (mange!) tilfælde, hvor koden skal afsluttes lige efter at den er startet, er der indført en forkortet skrivemåde:
<jsp:forward page="login1.html"/>
hvor man afslutter koden med /> til sidst. Dette svarer altså til
<jsp:forward page="login1.html"></jsp:forward>
Der findes en række implicit definerede objekter, som man altid har adgang til i en JSP-side. I det følgende vil disse objekter og deres vigtigste metoder blive beskrevet.
Objektet request (af type HttpServletRequest i pakken javax.servlet.http) repræsenterer anmodningen fra klienten (i en servlet bliver request-objektet overført i doGet()-metoden).
Objektet kan bruges til at få information om klienten (se eksempel i afsnit 2.4), formulardata fra klienten (se afsnit 3.2.5) og informationer om gemte cookier (se afsnit 3.6.5).
String getRequestURL()
Giver
den fulde URL på siden, der anmodes om, med maskinnavn og med
evt parametre
String getMethod()
Giver
anmodningsmetoden, som normalt er "GET". For formularer kan
"POST" også forekomme (en tredje mulighed, "PUT"
bruges til at sende filer til serveren). Se afsnit 3.6.4.
String getProtocol()
Giver
versionen af HTTP-protokollen klienten har anvendt, typisk "HTTP/1.1"
String getServerName()
Giver
værtsnavnet (eng.: host name), f.eks: "javabog.dk".
String getContextPath()
Giver
stien til webapplikationens rod.
String getServletPath()
Giver
stien på serveren på siden, der anmodes om (evt.
parametre er fjernet), relativt til webapplikationens rod. Med
application.getRealPath(request.getServletPath()) kan man finde den
absolutte sti og filnavnet til, hvor en JSP-side ligger fysisk på
harddisken (vist i afsnit 2.8.1).
String
getRemoteAddr()
og getRemoteHost()
Giver adressen (som IP-nummer eller evt med navn) på
klienten, der foretager anmodningen
String
getLocale()
Giver
brugerens foretrukne sprog (se eksempel på brug i
afsnit 2.4 og afsnit 13.2)
String
getParameter(String
parameternavn)
Giver værdien af en parameter fra klienten -
typisk fra en udfyldt formular, eller fra URLen. F.eks hvis en side
bliver kaldt som side.jsp?x=a siges det, at
den har parameteren x med værdien a og getParameter("x")
vil returnere strengen "a".
String[] getParameterValues(String
parameternavn)
Giver et array af værdier for et
parameternavn. HTTP-protokollen tillader, at parametre har flere
værdier. F.eks. vil side.jsp?x=aaa&x=bbb&x=ccc,
hvor parameteren "x" har tre værdier, få
getParameterValues("x") til at returnere et array med
værdier {"aaa", "bbb", "ccc"}. Se
afsnit 3.2.5.
Enumeration
getParameterNames()
Giver
en opremsning (eng.: enumeration) hvor
hvert element er navnet på en parameter.
F.eks vil
getParameterNames() give en
opremsning med "x", "y" og "y2" for en
side kaldt med: side.jsp?x=aaa&y=bbb?y2=ccc. Se et eksempel på
brug i afsnit 3.2.5.
Cookie[]
getCookies()
Giver
et array af cookier, der er sat hos klienten (netlæseren). Se
afsnit 3.6.5, hvor der også er et eksempel.
Objektet response (af type HttpServletResponse i pakken javax.servlet.http) repræsenterer svaret på anmodningen (i en servlet bliver response-objektet overført i doGet()-metoden). Objektet har information og data til klienten, herunder hvilke cookier der evt. skal sættes i klienten.
De vigtigste metoder er:
void
setContentType(String
indholdstype)
Sætter indholdstypen (MIME-typen).
Indholdstypen er fra systemets side sat til "text/html".
Ønsker
man at sende andre slags data, f.eks. binære, skal
indholdstypen sættes til "image/jpeg"
(jpeg-billeder), "audio/x-wav"
(wav-lyde) eller "application/x-dit-eget-valg" hvis det er
en helt selvopfunden indholdstype. Se et
eksempel i afsnit 2.8.4, Producere grafik fra JSP.
Tegnsættet
kan også sættes, f.eks. til Unicode
(indholdstype="text/html;charset=UTF-8").
void addCookie(Cookie
cookie)
Sætter eller tilføjer en cookie
hos klienten (netlæseren). Se afsnit 3.6.5.
void sendRedirect(String
url)
Omdirigerer klienten til en anden
side, den skal spørge på i stedet for denne side.
Adressen i url kan godt være relativ. Relateret til
<jsp:forward ... />, se afsnit 4.3, Omdirigering.
Objektet out (af type JspWriter i pakken javax.servlet.jsp) repræsenterer datastrømmen, der bliver sendt til klienten.
Det bruges lige som System.out:
out.println( "<h1>Hej verden!</h1>" );
Hver bruger får3 tildelt et session-objekt (af type HttpSession i pakken javax.servlet.http), der repræsenterer brugerens session. Det følger brugeren, lige meget hvilken side han/hun er inde på, og er derfor nyttigt til at huske data, der skal følge brugeren, f.eks om vedkommende er logget ind, sessions-ID og brugeroplysninger.
void setAttribute(String
nøgle, Object værdi)
Gemmer
et objekt under navnet nøgle.
Object getAttribute(String
nøgle)
Henter objektet under navnet nøgle.
void removeAttribute(String
nøgle)
Sletter referencen til objektet under navnet nøgle.
String[] getAttributeNames()
Giver
et array af alle nøgler der er defineret.
long
getLastAccessedTime()
Giver antal
millisekunder siden sessionen sidst var i
brug
int getMaxInactiveInterval()
Hvor
lang tid (i sekunder) der går før sessionen bliver smidt
væk, hvis den ikke bruges.
void setMaxInactiveInterval(int
sekunder)
Sætter hvor lang tid (i sekunder) der går
før en session, der ikke bruges, skal smides
væk.
void invalidate()
Smider
sessionen væk. Hvis den pågældende bruger besøger
en side igen, vil hun blive tildelt en ny session.
Objektet application (af type ServletContext i pakken javax.servlet) er fælles for alle sider og alle brugere af webapplikationen. Det kan bruges til at huske 'globale' data4.
Derudover kan den bruges til logning i webserverens log og til at fremfinde initialiseringsparametre fra web.xml, den globale konfigurationsfil for webapplikationen.
void setAttribute(String
nøgle, Object værdi)
Gemmer et objekt under navnet
nøgle.
Object getAttribute(String
nøgle)
Henter objektet under navnet nøgle.
void removeAttribute(String
nøgle)
Sletter referencen til objektet under navnet nøgle.
Enumeration
getAttributeNames()
Giver en opremsning af alle nøgler
der er defineret.
void log(String besked)
Logger en besked.
void log(String besked, Throwable undtagelse)
Logger en fejlmeddelelse og en supplerende undtagelse, hvis stakspor skrives til loggen.
String getRealPath(String url_i_webapp)
Giver den rigtige sti (på
harddisken) ud fra en URL i webapplikationen. Er nyttigt til at
indlæse og behandle filer fra harddisken, der befinder sig i
samme webapplikation, se afsnit 2.8.6.
Med
application.getRealPath(request.getServletPath()) kan man finde den
absolutte sti og filnavnet til hvor en JSP-side ligger fysisk på
harddisken (dette er vist i afsnit 2.8.1).
String getInitParameter(String navn)
Giver værdien af initialiseringsparameteren navn defineret i webapplikationens web.xml.
Enumeration getInitParameterNames()
Giver en opremsning af navnene på alle initialiseringsparametrene defineret i web.xml.
application-objektet giver også adgang til initialiseringsparametre fra web.xml.
Initialiseringsparametre er meget hensigtsmæssige til f.eks. at lægge navne på databasedrivere og database-URLer og lignende faste oplysninger. Det gøres ved at lave en indgang af typen context-param, f.eks. med databasedriveren, i web.xml:
<web-app> ... <context-param> <param-name>dbDriver</param-name> <param-value>com.mysql.jdbc.Driver</param-value> </context-param> ... </web-app>
Nu vil man i en JSP-side kunne hente værdien af initialiseringsparameteren dbDriver med:
String drv = application.getInitParameter("dbDriver");
hvorefter strengen drv vil have indholdet 'com.mysql.jdbc.Driver'. Der er et eksempel på dette i afsnit 8.5.8.
Fra en servlet (se afsnit 7.1, Servletter) er application-objektet tilgængeligt med:
ServletContext application = getServletContext();
hvorefter værdien fås som i en JSP-side med:
String drv = application.getInitParameter("dbDriver");
I afsnit 9.5, Eksempel: Login og brugeroprettelse, kan man se hvordan en ekstern klasse (en javabønne, vist i afsnit 9.5.2) kan bruge application-objektet til af få initialiseringsparametre.
Objektet config (af type ServletConfig i pakken javax.servlet) giver adgang til konfiguration i web.xml for den enkelte JSP-side eller servlet.
String getInitParameter(String navn)
Giver værdien af initialiseringsparameteren navn defineret for websiden i web.xml.
Enumeration getInitParameterNames()
Giver en opremsning af navnene på alle initialiseringsparametrene for websiden.
Eksempel
<web-app> .. <servlet> <servlet-name>jspside</servlet-name> <jsp-file>/jspside.jsp</jsp-file> <init-param> <param-name>tekst</param-name> <param-value>Dette er en tekst fra web.xml</param-value> </init-param> </servlet> </web-app>
Fra en JSP-side ville det kunne dette hentes med
config.getInitParameter("tekst")
som ville returnere strengen "Dette er en tekst fra web.xml".
Fra en servlet kunne teksten hentes med
getInitParameter("tekst"));
Dette objekt repræsenterer selve JSP-siden. Det er her taget med for en ordens skyld, selvom der ikke er meget man kan bruge det til i praksis.
Objektet exception (af type Exception) repræsenterer en undtagelse der opstod under kørsel af en JSP-side. Objektet er kun defineret på fejlmeddelelsessider (hvor side-direktivet isErrorPage er sat til "true").
Se også afsnit 10.4.5. Et eksempel findes i afsnit 4.4.
Objektet pageContext (af type PageContext i pakken javax.servlet.jsp) giver adgang til alle de andre implicit definerede objekter. Det kan være nyttigt at bruge, hvis man ønsker at have alle de implicitte objekter samlet i ét objekt - f.eks. til et metodekald.
JspWriter getOut()
Giver
out-objektet
HttpSession getSession()
Giver
session-objektet
Object getPage()
Giver
page-objektet
ServletRequest
getRequest()
Giver request-objektet
ServletResponse
getResponse()
Giver response-objektet
Exception getException()
Giver
exception-objektet
ServletConfig
getServletConfig()
Giver config-objektet
ServletContext
getServletContext()
Giver application-objektet
Forbedr ønskeseddel-eksemplet i afsnit 4.1.1, sådan at brugeren også kan angive sit navn. Navnet huskes efterfølgende i session-objektet og skrives i sidens titel.
Lav mulighed for, at brugeren kan slette alle sine ønsker (navnet forbliver)
Lav mulighed for, at brugeren kan 'logge ud'
(alle ønsker og navnet slettet).
Prøv at bruge
session.invalidate() (beskrevet i afsnit 4.5.4) for at smide
sessionen væk.
Lav et program, hvor man kan angive, hvem man vil stemme på til næste valg. Lad f.eks. brugeren vælge mellem 4 partier: Venstre, Konservative, Socialdemokratiet og SF.
Benyt radioknapper, sådan at man kun kan
stemme på et parti ad gangen.
Optæl stemmerne og vis
resultatet på skærmen hver gang brugeren har stemt.
Vink: Brug session- og application-objektet. Du kan søge
inspiration ved at kigge i afsnit 3.3, Appendiks: Typer af formularfelter.
Det kan til tider være svært at finde og rette fejl i JSP-sider. Her følger et par idéer til, hvordan du kan lokalisere og rette fejlene.
Har du en fejl, der driller, er det altid en god strategi, at prøve at isolere fejlen.
Ofte står man med en udgave af koden, der virker og en, der ikke virker. Prøv da 'del og hersk'-metoden: Find frem til fejlen ved langsomt at kopiere koden over (eller kommentere den ud og derpå kommentere den ind igen, bid for bid), sådan at du tilnærmer den kode, der virker, til den, der ikke virker.
Som nævnt i afsnit 2.3.1, Blandet Java og HTML, skal man være meget omhyggelig med at omkranse HTML-kode i {- og }-parenteser og sørge for, at der altid er lige mange {-startparenteser som }-slutparenteser.
Er der rod med parenteserne får man typisk fejlmeddelelserne:
'try' without 'catch' or 'finally' 'catch' without 'try' 'else' without 'if'
JSP-siden bliver internt oversat til en servlet, der så oversættes igen, fra .java-kildetekst til en binær .class-fil (beskrevet i afsnit 7.3, Avanceret: JSP-siders interne virkemåde).
Det er ofte svært at finde fejl i en JSP-side, fordi de linjenumre, der angives i fejlmeddelelsen, normalt henviser til linjenumre i servlettens .java-kildetekst i stedet for de oprindelige linjenumre i JSP-siden.
Udviklingsmiljøer som (den kommercielle udgave af) Borland JBuilder, Sun One Studio o.s.v. tager højde for dette og finder de oprindelige linjenumre og tjekker også løbende JSP-sidernes syntaks, så man med det samme ser fejlen det rigtige sted.
De oversatte servletter ligger normalt i work/-mappen i webserveren og hedder nogenlunde det samme som JSP-siderne (beskrevet i afsnit 7.3).
Når der opstår en fejl skrives en fejlmeddelelse i webserverens log. Det kan derfor være en god idé at kigge i disse.
Under Tomcat findes logfilerne i mappen logs/.
En snild måde under Linux til at kigge i alle logfilerne på én gang er fra kommandolinjen at skrive:
tail -f -n0 logs/*
Kommandoen 'tail' åbner herefter alle logfilerne og følger med i dem (-f), sådan at alle nye fejlmeddelelser skrives ud på skærmen.
Det er selvfølgelig afgørende for, om der er noget interessant at se i logfilerne, at dit program ikke undertrykker eventuelle fejl, men i stedet skriver dem ud.
Det er altså meget væsentligt at der ingen steder står:
try { ... } catch (Exception e) { } // Forkert! Fejlen undertrykkes!
men i stedet, i alle try-catch-blokke, står:
try { ... } catch (Exception e) { e.printStackTrace(); } // Udskriv stakspor i loggen
sådan at staksporet udskrives i loggen
Et eksempel på et stakspor er vist nedenfor. Staksporet fortæller præcist hvad der skete, men det er kun meget få dele af det (herunder fremhævet med fed), der har vores interesse. De andre afspejler nemlig blot måden webserveren og klassebibliotekerne fungerer på:
java.sql.SQLException: General error, message from server: "Table 'test.gaestebog' doesn't exist" com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:1825) com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:1020) com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:1109) com.mysql.jdbc.MysqlIO.sqlQuery(MysqlIO.java:1070) com.mysql.jdbc.Connection.execSQL(Connection.java:2027) com.mysql.jdbc.Connection.execSQL(Connection.java:1984) com.mysql.jdbc.Statement.executeQuery(Statement.java:1152) org.apache.jsp.JSP.kode.kapitel_05.gaestebog_jsp._jspService(gaestebog_jsp.java:58) org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:133) javax.servlet.http.HttpServlet.service(HttpServlet.java:856) org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:311) org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:301) org.apache.jasper.servlet.JspServlet.service(JspServlet.java:248) javax.servlet.http.HttpServlet.service(HttpServlet.java:856)
Oversat til dansk siger fejlmeddelelsen at:
Fejlen opstod i metoden checkErrorPacket(), på linje 1825 i filen MysqlIO.java.
Denne metode blev kaldt fra sendCommand(), på linje 1020 i filen MysqlIO.java.
... (flere metoder der var i gang med at kalde hinanden) ...
executeQuery() blev kaldt i metoden _jspService() på linje 58 i gaestebog_jsp.java
... (flere metoder der var i gang med at blive kaldt) ...
Som regel fortæller staksporet alene nok til at man kan finde fejlen. Kigger man på gaestebog.jsp (den kommer i afsnit 5.5, Eksempel - gæstebog) ser man at executeQuery() kun kaldes ét sted, og man har således fundet frem til stedet, hvor fejlen opstod.
Et man stadig i tvivl om, præcis hvor fejlen opstod (måske er der flere steder med kald til executeQuery()), må man bruge sin viden om, at det skete i linje 58 i gaestebog_jsp.java og kigge på den oversatte servlet (ligger normalt i work/-mappen i webserveren, se afsnit 7.3.1, Kigge i de genererede servletter).
Får du fejlen NoClassDefFoundError, er det et symptom på, at en klasse ikke kan findes. Det kan f.eks. ske, når du prøver at indlæse en databasedriver (der kan du også få 'No suitable driver') eller, hvis du bruger ekstra klassebiblioteker ud over de, der er defineret i standard Java JDK.
Ekstra klassebiblioteker og drivere fås næsten altid som JAR-filer (der er et ZIP-arkiv med en række .class-filer).
Der er mange muligheder for at løse problemet med, at en klasse ikke kan findes:
Hvis du arbejder i et udviklingsværktøj (f.eks. for at kunne oversætte nogle klasser/JSP-sider der bruger klasserne): Inkludér de rigtige biblioteker (eng.: libraries) med de JAR-filer, der indeholder klasserne (opret evt. et nyt bibliotek), i dit projekt.
Hvis programmet startes selvstændigt, evt. fra kommandolinjen: Sæt en henvisning til JAR-filen ind i miljøvariablen CLASSPATH
Hvis klasserne skal være kendt i ALLE Java-programmer (der kører på en bestemt JDK): Kopier JAR-filerne over i JDK-et, under jre/lib/ext/-mappen
Hvis klasserne skal være kendt i ALLE webapplikationer (der kører i en bestemt Tomcat webserver): Kopier JAR-filerne over i Tomcats common/lib/-mappe
Hvis klasserne skal være kendt i en webapplikation, sådan at denne webapplikation også kunne flyttes over på en anden server: Kopiér JAR-filerne over i webapplikationens WEB-INF/lib/-mappe (opret evt mappen - se også afsnit 7.4).
I princippet kan alle mulighederne være lige gode til at løse problemet, men når du har fået det til at virke, så brug lidt tid på at overveje om løsningen er den rigtige for dig (det er f.eks. lidt kedeligt hvis du ofte skifter JDK og du netop har lagt databaseklasserne ind i dit JDK).
1Omdirigering
skal ske så tidligt på siden, at man kan være
sikker på, at der ikke er genereret så meget HTML, at
bufferen er løbet fuld og HTTP-hovedet (se afsnit 3.7) og den
første del af data således er sendt til klienten. Når
først data er sendt til klienten, kan webserveren ikke
'fortryde' og i stedet omdirigere til en anden side.
For
servletter er der ingen output-buffer, så
i servletter skal omdirigering ske inden der har været noget
output overhovedet.
2Som vi senere skal se, i afsnit 7.3, Avanceret: JSP-siders interne virkemåde, bliver JSP-sider lavet om til en metode i en klasse. Man kan undgå at den efterfølgende kode i metoden udføres ved at returnere fra metoden, med 'return;'. I afsnit 10.4.4 er der et eksempel på dette.
3Denne
opførsel kan slås fra ved at sætte
session="false" i sidedirektivet med
<%@ page
session="false" %>
4En mindre hensigstmæssig måde kunne være at bruge en klassevariabel i en separat klasse.