Utveckling av en enkel webapplikation (chat)
På den här sidan byggs, i små steg, en liten webapplikation
som är en enkel chat-server. För enkelhetens skull struntar vi
i säkerheten, det kommer inte att krävas någon inloggning
för att se konversationen i ett visst chat-rum. Det kommer också
gå att åstadkomma fel genom att tex titta på sidorna
i fel ordning. Den intresserade kan hitta exempel på hur säkerhetsfrågorna
kan lösas i avsnittet En komplett webapplikation (number guess
game) på sidan Java
Servlets och JavaServer Pages.
Steg 1
Först måste vi göra en enkel design av webapplikationen.
Vilka komponenter ska ingå och vilket ansvar ska de ha? Vilka
vyer ska finnas och i vilken ordning ska de visas?
Lösningsförslag
Det finns många bra (och många dåliga) lösningar.
Resten av den här övningen utgår från denna design,
chat-webapp.gif.
Steg 2
Vi börjar med choose-chatroom.jsp. Det första
steget blir att över huvud taget kunna få kontakt med en jsp
vi deployat, dvs en "hello world"-uppgift. Underskatta inte denna del
av arbetet, det är förmodligen det största steget som
måste tas på en gång.
Skriv alltså en jsp som heter choose-chatroom.jsp
men inte gör något annat än att skriva ut en kort statisk
html-sida. Deploya den och kolla att du kan surfa till den.
Lösningsförslag
choose-chatroom.jsp
web.xml
Steg 3
Nu kommer en till "hello world", men denna gång med en servlet
(som i sinom tid ska bli FrontController).
Skriv en servlet som heter FrontController och inte gör
något annat än att skriva ut en kort text till System.out.
Deploya den och kolla att du kan surfa till den.
Lösningsförslag
FrontController.java
web.xml
Steg 4
Nästa steg blir att lägga in ett formulär i choose-chatroom.jsp,
det kan till exempel se ut som nedan:
Tills vidare struntar vi i vad som ska hända när användaren
klickar på OK, du kan alltså låta bli att ange attributet
action i form-taggen. Om du anger method="get"
(i stället för post) kan du se parametrarna som skickas
när du klickar på OK i url:en.
Lösningsförslag
choose-chatroom.jsp
Steg 5
Nu är det dags att koppla ihop choose-chatroom.jsp
med FrontController. För att göra det måste vi
ange action="FrontController" i formuläret i jsp:n. Om
du vill kan du också passa på att ändra method
till post. En skillnad är att parametrarna inte visas (eller
går att skriva in) i url:en, vilket kanske är snyggare, en annan
är att post klarar fler tecken än get. Ur debug-synpunkt
kan det i och för sig vara bra att fortsätta använda get
just därför att parmetrarnas värde tydligt syns. Enligt HTML-specifikationen
ska post användas om det blir några sidoeffekter till
följd av att formuläret sänds. Exempel på sidoeffekter
är att en databas uppdateras eller att anvädaren anmäls till
någon mailinglista eller köper något. Följer vi detta
ska post användas eftersom användaren börjar delta
i en konversation när formuläret sänds.
Dessutom lägger vi till lite kod i servleten som skriver ut
parametrarnas värde på System.out. På det viset
kan vi kolla att vi kan skicka och ta emot parametrar. Om du låter
både doGet() och doPost() i servleten anropa en
och samma (privata) metod där alla operationer görs slipper du
problem om du senare vill byta mellan get och post i
formuläret.
I och med denna övning är choose-chatroom.jsp
färdig.
Lösningsförslag
FrontController.java
choose-chatroom.jsp
Steg 6
Skriv chat.jsp med "hårdkodad konversation", dvs det
ska inte gå att chatta men vi kan labba lite med textareor i html
genom att hårdkoda en fingerad konversation. Formuläret i chat.jsp
kan till exempel se ut så här:
Använd, som när vi började med choose-chatroom.jsp,
get i stället för post så kan du se parametrarna.
Lösningsförslag
chat.jsp
Steg 7
Koppla ihop chat.jsp med FrontController genom
att lägga till action="FrontController" i formuläret.
Lägg även till flödeskontroll i FrontController.
I den här lilla applikationen blir det inte mer flödeskontroll
än så här:
- Efter choose-chatroom.jsp kommer chat.jsp.
- Efter chat.jsp kommer chat.jsp igen.
- Om action saknas visas choose-chatroom.jsp.
Det kan vara bra att ha kvar debugutskriften av request-parametrar
i FrontController. För att inte missa något kan vi
ändra i den så att alla parametrar skrivs ut utan att vi behöver
namnge dem.
Lösningsförslag
chat.jsp FrontController.java
Steg 8
Nu är det dags att börja med filhantering. Det är meningen
att varje konversation ska sparas i en egen fil (vi hoppar över databaser).
Vi skriver en JavaBean som hanterar filen. Denna klass blir vår "modell".
Genom att skapa en ny klass för detta ändamål kan FrontController
vara en renodlad controller och JSP:erna behöver inte vara något
annat än vyer.
Vi börjar med att fundera på det publika gränssnittet.
Vår böna bör ha dessa metoder:
public void setFile(File file) //Talar om vilken fil konversationen ska sparas i.
public void setNickName(String nickName) //Talar om vilket namn som ska skrivas framför inläggen i konversationen.
public void addLine(String line) //Lägger till en replik (dvs en sträng) sist i filen.
public void startReading() //Gör att nästa anrop av getNextLine() returnerar första repliken i konversationen (dvs första raden i filen).
public boolean hasMoreLines() //Returnerar true om nästa anrop av getNextLine() kommer att returnera ytterligare en replik.
public String getNextLine() //Returnerar nästa replik i konversationen eller null om vi nått konversationens slut.
Det är en bra ide att skriva en main()-metod i bönan
som testar alla dess publika metoder. På så sätt kan vi
prova den utan att behöva blanda in resten av webapplikationen. Ett
sådant test ska antingen tala om att alla tester lyckades eller vilket
test som misslyckades. Det ska inte skriva ut något värde som
måste tolkas för att avgöra om testen lyckades.
Lösningsförslag
ConversationBean.java
Steg 9
Nästa steg blir att koppla ihop bönan med FrontController.
När användaren kllickar på START_CHAT i choose-chatroom.jsp
ska FrontController anropa metoderna setFile() och setNickName().
När användaren klickar på SEND i chat.jsp
ska addLine() anropas. Vi väntar med att låta chat.jsp
läsa från filen.
Två frågor vi måste besvara är vad filen ska
heta och var den ska ligga. Alla servletar (och JSP:er) har tillgång
till en katalog för temporära filer. Filer som placeras där
finns så länge webapplikationen finns men får automatiskt
tas bort sedan. Det är lämpligt att placera filerna där.
Vi får tag i den katalogen med följande rad:
File tempDir = (File)getServletContext().getAttribute("javax.servlet.context.tempdir");
Med Orion heter tempkatalogen (om inget annat angetts) ORION_HOME/application-deployments/default/chat/temp
om webapplikationen finns i katalogen chat. Med nedanstående
rad skapas filen test.txt i temporärkatalogen:
new File(tempDir, "test.txt");
Angående namnet är ett förslag att filen får ha
samma namn som chatrummet.
Ytterligare en fråga är hur vi ska spara ConversationBean-objektet.
Det måste vara i sessionsobjektet eftersom det ska överleva ett
request men inte vara gemensamt för alla webapplikationens användare.
Lösningsförslag
FrontController.java
Steg 10
Nu är vi äntligen framme vid sista steget, att låta
chat.jsp läsa från filen. För det ändamålet
ska getConversation() i ConversationBean anropas. Tyvärr
är vi tvugna att skriva lite Java-kod i chat.jsp eftersom
det kommer att krävas en loop som läser alla rader ur den Collection
som returneras av getConversation(). Vi hade sluppit det om vi i
stället låtit getConversation() returnera en jättesträng
med hela konversationen. Nackdelen med den lösningen är att vi
då börjar placera in formatering av vyerna (dvs strängens
utseende) i modellen (dvs ConversationBean).
Lösningsförslag
chat.jsp
Teckenuppsättningar och åäöÅÄÖ
När vi ska hantera ett tecken som inte ingår i 7-bits ASCII
(tex å, ä, ö, Å, Ä eller Ö) riskerar vi problem
med teckenuppsättningarna. Det viktiga är att se till att alla
browsrar använder samma teckenuppsättning som servern. Det är
ingen bra ide att anpassa servern eftersom vi inte kan lita på att
alla browsrar beter sig lika.
Orion 2.0.2 använder default UTF-8 (som egentligen inte är en
teckenuppsättning utan en transformation av Unicode). Vi kan säga
åt browsrarna att använda UTF-8 genom att lägga till attributet
accept-charset="UTF-8" i alla HTML-formulär. Detta innebär
att våra jsp:er ser ut så här:
choose-chatroom.jsp
chat.jsp
Att-göra lista
Det är inte direkt en kommersiellt gångbar chatserver vi skrivit...
Några problem som återstår att lösa:
- Låsning av filen så vi inte riskerar läsa halva
repliker.
- Det borde inte vara nödvändigt att klicka på reload
för att se nya inlägg. Det kan lösas med autoreload eller
med javascript.
- Inloggning
- Felhantering