Servlet-urile sunt aplicatii Java aflate pe server si executate de catre acesta. Serverul de Web, la executarea servlet-ului, trimite înapoi browser-ului un fisier HTML, pe care acesta în afiseaza corespunzator; în particular, browser-ul nu trebuie sa stie nici macar ce înseamna un servlet.
În modalitatea clasica, serverul de Web primeste - de exemplu - o cerere de tipul:
GET nume_fisier
si întoarce fisierul respectiv. Aici nume_fisier este numele unei clase (servlet) a carei executare întoarce browser-ului un fisier HTML pe care browser-ul îl afiseaza clientului. Acesta poate completa unele informatii si retrimite o cerere noua serverului de Web, care prelucreaza informatiile primite si retrimite rezultatul din nou sub forma unui document HTML. Procesul se poate repeta de oricâte ori. Un pas al acestui proces poarta numele de tranzactie. Precizam ca serverul nu pastreaza datele de la o tranzactie la alta decât (fireste) daca le stocheaza explicit (de exemplu într-un fisier sau într-o baza de date).
Partea din serverul de Web care asteapta cereri (în cazul nostru cereri de executare a unui servlet) este numita demon HTTP, deoarece are caracteristicile unui fir de executare demon. Serverul poate efectua "simultan" tranzactii cu mai multi clienti, pornind pentru fiecare un fir de executare.
Mai mentionam ca schimbul de informatii între server si fiecare client se face în esenta prin socket-uri, dar la un nivel superior si în mod transparent pentru utilizator.
Servlet-urile nu sunt subiectul unor restrictii, asa cum se întâmpla de exemplu cu applet-urile. Ele creaza documente HTML dinamice prin succesiunea de tranzactii descrise mai sus. Independenta de platforma a limbajului Java se extinde în mod natural si logic asupra servlet-urilor (spre deosebire de script-urile CGI care sunt independente de platforma si care sunt în plus mult mai lente).
Pentru lucrul cu servlet-uri, vom crea în directorul nostru de lucru User din:
C:\Tomcat3\jakarta-tomcat-3.3.2\webapps
urmatoarea structura de directoare:
User
WEB-INF (*)
classes (**)
si vom copia în ( am copiat fisierul web.xml din
C:\Tomcat3\jakarta-tomcat-3.3.2\webapps\examples\WEB-INF
Aplicatiile noastre (servlet-urile) vor fi plasate în
Mai sunt necesare urmatoarele:
Înainte de compilarea servlet-urilor facem setarea:
set classpath=
C:\Tomcat3\jakarta-tomcat-3.3.2\lib\common\servlet.jar;.
Varianta: adaugam calea de mai sus în comanda set classpath din autoexec.bat
Daca vrem sa executam C.class obtinuta prin compilarea unui servlet aflat în directorul classes, deschidem de pe client Internet Explorer si specificam:
https://server:8080/User/servlet/C (***)
cu observatia ca servlet nu este director, ci informatie ca lucram cu servlet-uri; clasa C va fi cautata în ...\User\WEB_INF\classes
Daca în cream un pachet P având în interiorul sau clasele publice A (principala), B si C, atunci:
clasele A B C vor începe cu: package P;
din interiorul pachetului P la setarea de mai sus adaugam sufixul:
si compilam normal clasa A
devine: https://localhost:8080/User/servlet/P.A
Prezentam în continuare un prim exemplu, cu câteva explicatii necesare, urmând ca unele interfete, clasele si metode referitoare la servlet-uri sa fie descrise în continuare în detaliu.
Consideram urmatorul servlet:
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class Salut extends HttpServlet
care va fi invocat din browser prin:
https://server:8080/User/servlet/Salut?nume=Vasile&val=20
prin care se cere executarea servlet-ului Salut cu precizarea a doi parametri (siruri de caractere): nume cu valoarea Vasile si val cu valoarea În mod implicit, cererea este de tipul GET; corespunzator, va fi invocata metoda doGet a servlet-ului.
Browser-ul va afisa:
Furnizam în continuare informatiile necesare:
Salut este un servlet deoarece extinde clasa HTTPServlet
Pentru lucrul cu servlet-uri trebuie incluse declararile:
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
Servlet-ul Salut contine metoda doGet care este invocata la o cerere HTTP de tipul GET
Variabilele resp si req sunt obiecte de tipul indicat, care corespund cererii primite de la client, respectiv raspunsului care va fi transmis clientului.
Prin invocarea metodei setContentType se precizeaza ca tipul MIME al raspunsului întors clientului este un document HTML.
Prin invocarea metodei getWriter obiectul resp creeaza un flux de iesire out în care va fi introdus documentul HTML.
Obiectul req invoca metoda getParameter pentru a capta în variabilele locale nume si val valorile parametrilor din cererea HTTP care au numele nume si val
Variabila html de tip String primeste ca valoare sirul de caractere ce reprezinta documentul HTML ce va fi transmis clientului.
html este înscris în out
sirul de caractere "O.K" este afisat în fereastra deschisa de Tomcat.
Observatie. Sa presupunem ca dupa invocarea servlet-ului de catre client, pe server modificam acest servlet si îl compilam. Nu se garanteaza ca, la o noua invocare a servlet-ului de catre client, va fi folosita noua forma! În schimb, noua forma a servlet-ului va deveni sigur efectiva dupa oprirea si repornirea serverului.
În exemplul de mai sus, clientul a trimis (prin intermediul browser-ului) o cerere severului de Web, care a raspuns cu un document HTML, pagina Web respectiva fiind afisata de browser.
Dorim însa mai mult si anume sa realizam un dialog "continuu" între client si server. O posibilitate consta în a folosi formularele din HTML, varianta studiata în acest paragraf. O alta posibilitate, dezvoltata într-un paragraf ulterior, consta în a folosi applet-uri pe partea de client.
Formulare
Formularele stau la baza crearii, cu ajutorul servlet-urilor, a paginilor HTML dinamice si a procesarii de catre server a datelor transmise de client prin intermediul lor. Pot fi create formulare multiple, dar independente. Ele au forma:
<form method=metoda action=clasa>
elemente de control
</form>
Despre metoda si clasa vom vorbi mai târziu.
Principalele elemente de control sunt:
câmpurile de text:
<input name=nume type=text value=val maxlength=n >
ariile de text:
< textarea name=nume cols=m rows=n >
butoanele de validare:
<input name=nume type=checkbox value=val checked >
permit alegerea multipla (a mai multor optiuni), care sunt siruri de caractere. Toate optiunile trebuie sa aiba acelasi nume nume si pentru fiecare poate fi precizat, prin prezenta lui checked, daca este selectat.
butoanele radio:
<input name=nume type=radio value=val checked >
difera de butoanele de validare prin aceea ca exact o optiune poate fi aleasa.
meniuri (liste derulante):
<select>
<option value=val selected> text </option>
<option value=val selected> text </option>
</select>
unde optiunile initial selectate sunt precizate prin selected.
butoanele de comanda:
<input name=nume type=tip value=val >
unde tip poate fi:
reset : câmpurile formularului revin la valorile initiale;
submit : câmpurile formularului sunt trimise catre server prin sirul de parametri;
push : este întreprinsa actiunea definita de autorul formularului.
nume de fisier:
<input type=file name=nume >
unde nume este numele unui fisier care este transmis; va aparea si un buton Browse a carui actionare va permite selectarea fisierului prin navigare în structura de directoare.
Exemplu. Sa consideram urmatorul document HTML cu numele form.html
<html>
<head> <title> Formular </title> </head>
<body>
<form method=GET action=???>
<p> Numele <input type=text name="Nume">
<p>
Ai studiat Informatica in liceu?
<input type=radio name="radio" value="Da" checked>
Da    
<input type=radio name="radio" value="Nu"> Nu
<p>
Selecteaza cursurile favorite <br>
<input type=checkbox value="1"> Programare <br>
<input type=checkbox value="2" checked> Algoritmi <br>
<input type=checkbox value="3" checked>
Baze de date <br>
<p>
Forma de invatamant:
<Select name="valuta">
<option value="Inf"> Informatica </option>
<option selected value="MI"> Mat-Inf </option>
<option value="Col"> Colegiu </option>
</Select>
<p> Fisier catre server:<br>
<input type=file value="fisier" >
<p>
Butoane:<br>
<input type=reset name="Exit" value="Iesire">
<input type=submit name="Cerere" value="Cerere">
</form>
</body>
</html>
în care despre valoarea câmpului action din tag-ul form vom vorbi ulterior.
Browser-ul va afisa:
Trimiterea de date catre server
Reamintim ca marcajul form are forma:
<form method=metoda action=clasa>
Exista doua modalitati de trimitere (corespunzatoare celor doua tipuri de cereri HTTP), dupa cum metoda este:
GET datele din formular sunt adaugate în URL-ul trimis, sub forma de perechi
(nume,valoare). Ele vor fi accesibile servlet-ului prin metodele:
getQueryString, getParameter si getParameterValues;
POST : datele sunt înglobate în antetele cererii. Ele vor fi accesibile servlet-ului prin
metoda getParameterValues sau prin citire din ServletInputStream.
În functie de metoda aleasa, servlet-ul specificat prin clasa va executa metoda doGet sau metoda doPost. Alte detalii despre cele doua tipuri de cereri HTML, respectiv despre metodele doGet si doPost, vor fi furnizate ulterior.
Exemplu. Dorim sa trimitem siruri de caractere catre server.
Vom folosi un formular care contine doar un câmp de text si un buton submit. La completarea câmpului de text si apasarea butonului, va fi întors mesajul transmis.
import java.io.*;
import javax.servlet.*; import javax.servlet.http.*;
public class TF extends HttpServlet
Observatie. O alternativa la executarea servlet-ului TF în modul precizat este de folosi urmatorul document HTML:
<html>
<head><title>Camp de text</title>
</head>
<body>
<form method=GET
action=https://localhost:8080/User/servlet/TF>
<p><input type=text name=tf maxlength=20>
<p><input type=submit name=Buton value=Apasa>
</form>
</body>
</html>
Reamintim ca acest document (fie TF.html numele sau) va fi plasat în mod tipic pe serverul de Web în directorul User, iar el va fi cerut de client prin:
https://server:8080/User/TF.html
Pachetul javax.servlet
Clase:
public abstract class ServletOutputStream
public abstract class GenericServlet
implements Servlet, Serializable
Pachetul javax.servlet.http
Clase:
Prezentarea va fi facuta tratând separat clasele/interfetele referitoare la: servlet-uri, cereri, raspuns, fluxuri.
Servlet-uri
Ne vom ocupa în principal de clasa HTTPServlet, care extinde GenericServlet, care la rândul sau implementeaza interfata Servlet
Precizam ca GenericServlet defineste un servlet independent de protocol, iar HttpServlet este specifica protocolului HTTP.
Restrângem prezentarea la doua metode de serviciu ale servlet-urilor:
protected void doGet(HttpServletRequest req,
HttpServletResponse resp)
throws ServletException, IOException
Aceasta metoda este invocata de server în momentul în care primeste o comanda HTTP de tip GET. În mod tipic, metoda trebuie ca, folosind informatia din parametrul req, sa întreprinda actiunile corespunzatoare si sa plaseze rezultatele în fluxul de iesire creat pe baza parametrului resp
protected void doPost(HttpServletRequest req,
HttpServletResponse resp)
throws ServletException, IOException
Aceasta metoda difera de doGet prin aceea ca este invocata de server în momentul în care primeste o comanda HTTP de tip POST. Alte diferente vor fi prezentate în continuare.
Ciclul de viata al unui servlet
Servlet-ul este un program Java care ruleaza în cadrul unui server de Web. De fapt serverul de Web creaza un container în cadrul caruia este executat servlet-ul. Pentru simplificare, vom considera ca acest container este chiar serverul de Web.
În literatura de specialitate apare afirmatia "servlet-ul este un applet pe partea de server". Afirmatia se bazeaza pe analogia între ciclul de viata al unui applet si ciclul de viata al unui servlet.
Principalele metode care guverneaza lucrul cu servlet-uri sunt:
Metoda init()
este declarata în clasa GenericServlet prin:
public void init() throws ServletException
Metoda este invocata numai la crearea servlet-ului. Un servlet este creat prima data când un utilizator invoca URL-ul corespunzator servlet-ului. În concluzie, la invocari ulterioare metoda init() nu mai intra în actiune.
Metoda service()
este declarata în clasa GenericServlet prin:
public abstract void service(ServletRequest req,De fiecare data când serverul de Web primeste o cerere pentru un servlet, lanseaza un nou fir de executare si invoca metoda service Metoda detecteaza tipul cererii HTTP si invoca în mod corespunzator una dintre metodele de serviciu doGet si doPost.
Putem evita metoda service furnizând implementari pentru metodele doGet si doPost, asa cum vom proceda de obicei în continuare. Totusi metoda service îsi gaseste utilitatea în situatia existentei unei conditii în functie de care se invoca doGet sau doPost.
Daca dorim ca ambele tipuri de cerere sa fie tratate identic, specificam în doGet actiunea corespunzatoare si folosim metoda doPost urmatoare:
public void doPost(HttpServletRequest req,
HttpServletResponse resp)
throws ServletException, IOException
Metoda destroy()
este declarata în clasa GenericServlet prin:
public void destroy()
Aceasta metoda este invocata de serverul de Web la cererea administratorului sau în cazul în care servlet-ul nu primeste cereri o perioada îndelungata de timp. În metoda sunt precizate de obicei eliberari de resurse alocate prin init(), ca de exemplu întreruperea unei conexiuni cu o baza de date. Instanta servlet creata prin init() este distrusa.
Recapitulând, ciclul de viata al unui servlet poate fi figurat astfel:
Este important de retinut ca cereri adresate unui servlet de catre mai multi clienti au ca efect crearea pentru fiecare client a unui fir de executare care invoca metoda service a unicei instante a servlet-ului. În consecinta câmpurile servlet-ului sunt comune tuturor firelor, chiar daca nu sunt declarate cu modificatorul static. Rezolvarea problemelor de concurenta ce pot aparea prin folosirea de catre mai multe fire a unor resurse comune (câmpurile servlet-ului) cade în sarcina programatorului.
Exemplificam cele de mai sus prin prezentarea unui servlet care numara de câte ori a fost invocat.
import java.io.*;
import javax.servlet.*; import javax.servlet.http.*;
public class Accese extends HttpServlet
La fiecare cerere, atât în fereastra Tomcat-ului cât si pe fiecare client apare numarul total de invocari ale servlet-ului.
Într-un capitol urmator vom arata cum putem tine evidenta numarului de cereri pentru fiecare client.
Cereri
Prezentam în continuare câteva metode ale interfetei HTTPServletRequest, precizând care dintre ele este mostenita de la interfata ServletRequest
Interfata HTTPServletRequest defineste un obiect care permite servlet-ului sa identifice informatia trimisa de client. Containerul servlet creaza un obiect de acest tip, continând cererea catre servlet, pe care îl transmite ca argument metodelor de serviciu. Sunt incluse aici numele si valorile parametrilor din cerere, atribute si fluxul de intrare.
public String getMethod()
întoarce numele metodei continuta în cerere;
public String getQueryString()
întoarce sirul de parametri ce urmeaza caii din URL. Daca nu apar parametri, metoda întoarce null. Cum de obicei prima tranzactie se face fara parametri, metoda poate fi folosita pentru a identifica aceasta prima tranzactie;
public String getRequestURI()
întoarce portiunea din cerere dintre numele protocolului si querystring
public String getContextPath()
întoarce portiunea din URI care precizeaza contextul cererii;
public String getServletPath()
întoarce portiunea din URI care invoca servlet-ul.
Urmatoarele metode sunt mostenite de la interfata ServletRequest
public String getRemoteAddr()
întoarce adresa IP numerica a clientului;
public String getServerName()
întoarce adresa IP simbolica a serverului;
int getServerPort()
întoarce portul prin care comunica serverul;
public String getContentType()
întoarce tipul MIME al cererii sau null (daca acest tip nu a fost precizat). Pentru anumite tipuri MIME, sirul de caractere întors contine si alte informatii; de exemplu pentru tipul "multipart/form-data" mai este inclus si sirul de carcatere ce constituie linia separatoare (vezi paragraful referitor la transmiterea de fisere între client si server).
Metoda getInputStream va fi prezentata atunci când vom vorbi despre fluxuri.
Urmatoarele doua metode permit accesarea parametrilor din sirul de parametri adaugati comenzii HTTP, de exemplu prin actionarea unui buton de tip submit
public String getParameter(String nume)
întoarce valoarea parametrului-cerere nume, sau null daca acesta nu exista. În cazul în care parametrului-cerere îi sunt asociate mai multe valori (caz în care de fapt trebuie folosita metoda getParameterValues), metoda întoarce prima informatie întoarsa de metoda getParameterValues.
public String[] getParameterValues(String nume)
întoarce un tablou cu valorile asociate parametrului-cerere nume. Metoda este folosita atunci când parametrul-cerere are mai multe valori (de exemplu cele selectate în cadrul unui meniu). Daca nume nu exista, este întors null
Exemplu. Consideram urmatorul servlet:
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class Ex1 extends HttpServlet
La executare, browser-ul va afisa:
RemoteAddr : 127.0.0.1
Method : GET
ServerName : localhost
ServerPort : 8080
RequestURI : /User/servlet/Ex1
ContextPath : /User
ServletPath : /servlet/Ex1
ContentType : null
Interfetele:
public interface ServletResponse
public interface HttpServletResponse extends ServletResponse
definesc un obiect prin intermediul caruia servlet-ul transmite un raspuns clientului, raspuns dirijat catre fluxul de iesire atasat acestui obiect. HttpServletResponse furnizeaza în plus functionalitate HTTP.
Dintre metode mentionam doar urmatoarele:
public void setContentType(String tip)
în care tip precizeaza versiunea protocolului MIME (de exemplu "text/html" folosit la transmiterea raspunsului;
public void addHeader(String s1, String s2)
prin care se adauga antetul (header-ul) cu numele s1 si cu valoarea s2 Un exemplu va fi prezentat în capitolul referitor la transmiterea de fisiere.
Pentru fluxurile de intrare este folosita clasa:
public abstract class ServletInputStream extends InputStream.
Un flux de intrare poate citi date la nivel de octet, furnizând în plus metoda readLine pentru citirea de linii:
public int readLine(byte[] octeti, int depl, int nr)
citeste în octeti câte o linie din fluxul de intrare, plecând de la pozitia curenta, cu deplasarea depl. Metoda se termina fie la detectarea sfârsitului de linie dupa citirea a maximum nr octeti (caz în care rezultatul întors este numarul de octeti cititi), fie la citirea a nr octeti fara detectarea sfârsitului de linie (caz în care întoarce valoarea -1).
Pentru un obiect-cerere (de unul dintre tipurile ServletRequest sau HttpServletRequest) putem regasi fluxul de intrare prin invocarea urmatoarei metode a clasei ServletRequest
public ServletInputStream getInputStream()
Pentru fluxurile de iesire este folosita clasa:
public abstract class ServletOutputStream extends OutputStream
Pentru un obiect-raspuns (de unul dintre tipurile ServletResponse sau HttpServletResponse) putem regasi fluxul de iesire prin invocarea uneia dintre metodele urmatoare, ambele folosite daca dorim ca transmiterea sa se faca la nivel de octet:
public ServletOutputStream getOutputStream()
din interfata ServletResponse
public PrintWriter getWriter()
din interfata HttpServletResponse. Invocarea metodei flush() încheie acest raspuns. Setarea versiunii MIME (prin setContentType) trebuie facuta înainte de a folosi acest flux de iesire.
Exemplu. Dorim sa trimitem serverului un nume de fisier, folosind metoda POST. Servletul va folosi fluxul sau de intrare pentru a citi din el numele fisierului.
Clientul va cere prin intermediul browser-ului fisierul File.html urmator:
<html>
<head> <title> Formular </title> </head>
<body>
<form method=POST
action=https://adresa_server:8080/User/servlet/File>
<p> Fisier catre server:<br>
<input type=file name=fisier >
<p> Buton:<br>
<input type=submit name=Buton value=Apasa>
</form>
</body>
</html>
În fereastra browser-ului vor aparea un câmp de text, un buton cu eticheta Browse si un al doilea buton cu eticheta Apasa
Actionarea butonului Browse permite alegerea unui fisier al carui nume (cu calea completa) este înscris în câmpul de text. Actionarea butonului Apasa transmite serverului aceste informatii, care le prelucreaza prin urmatorul servlet:
import java.io.*; import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class File extends HttpServlet
Sunt necesare câteva explicatii.
S-a citit în s informatia din fluxul de intrare. Aceata informatie este prelucrata cu StringTokenizer, pentru care am prevazut delimitatorul '&'
Presupunând ca am ales sa trimitem numele de fisier autoexec.bat din C:, informatia afisata de server în fereastra Tomcat va avea forma:
fisier=C%3A%5CAUTOEXEC.BAT
Buton=Apasa
deoarece, la transmiterea informatiei, caracterul este înlocuit prin sirul de caractere "%3A", iar caracterul este înlocuit prin sirul de caractere "%5C", conform schemei de codare implicite.
|