7.

TEHNOLOGIA JAVA SERVLET

7.1. Conceptul de servlet; containere de servlet-uri
7.1.1. Locul servlet între tehnologiile Web
Foarte simplu spus, un servlet [49] este un program Java care rulează pe un server Web şi care construieşte (dinamic) pagini Web. Din punct de vedere funcţional, un servlet face acelaşi lucru ca şi un program CGI, descris în 4.7.1. Să vedem mai întâi unde se află servlet în panoplia tehnologiilor Web actuale. Există mai multe lucrări care tratează acest subiect. Părerea noastră este că lucrarea [51] face o analiză pertinentă a subiectului, motiv pentru care ne-o însuşim. Mai mult, ceea ce vom prezenta în această secţiune este în mare măsură preluat din [51]. Pentru realizarea de pagini Web dinamice şi pentru asigurarea unei interacţiuni bidirecţionale între browser şi server-ul Web, ultimii 10 ani au consacrat mai multe tehnologii. Următoarele sunt cele mai răspândite şi le enumerăm aproximativ în ordinea apariţiei lor. 1. Common Gateway Interface (CGI) Este prima tehnologie de acest tip. În 4.7.1 am prezentat această tehnologie. 2. ColdFusion [51] Este un produs al corporaţiei Allaire. În esenţă este vorba de extinderea HTML cu o serie de tag-uri noi pentru operaţii speciale, mai ales pentru interogarea bazelor de date. Tehnologia şi-a avut locul ei în istorie şi a fost atractivă (doar) până când au apărut şi alte tehnologii Web. 3. Server-Side Includes (SSI) [23] Oferă posibilitatea execuţiei unor script-uri sau a altor acţiuni în partea de server, prin specificarea unor comenzi incluse direct în codul HTML. Din cauza multor slăbiciuni, mai ales pe partea de securitate, utilizarea SSI este din ce în ce mai rară. 325

4.

Server-Side JavaScript (SSJS) [51] Limbajul JavaScript este destinat în principal acţiunilor la nivel de client. SSJS este o extensie a limbajului JavaScript pentru a descrie clase care să acţioneze la nivel de server.

5.

PHP (Personal Home Page, sau mai recent Professional Hipertext Processor) Este o tehnologie open-source extrem de atractivă şi de răspândită în prezent. PHP oferă facilităţi simple de descrierea unor acţiuni la nivel de server, operând inclusiv cu conceptul de sesiune şi oferind o serie de funcţiuni interne legate de acces la fişiere şi baze de date.

6.

Servlet Este o tehnologie introdusă de Sun în 1996. O vom trata în acest capitol.

7.

Java Server Pages (JSP) Este o extensie a tehnologiei servlet, bazată pe script-uri, de care ne vom ocupa în capitolul următor.

8.

Active Server Pages (ASP) Este o tehnologie bazată pe script-uri, promovată de Microsoft pentru a opera pe platforme Windows. Lucrează în principal în tandem cu server-ele Web produse de Microsoft, adică cele de tip IIS. Se încearcă adaptarea şi pentru server-ele Web de tip Apache. Următoarea tehnologie o include.

9.

Active Server Pages .NET (ASP.NET) Face parte din iniţiativa .NET a Microsoft. Introduce elemente noi faţă de ASP. Este interesant faptul că la baza .NET stă limbajul C#, care este extrem de asemănător cu limbajul Java. De asemenea, executivul .NET - numit Common Language Runtime (CLR) este extrem de asemănător cu Java Virtual Machine (JVM)!

În ultima vreme, piaţa programării Web a fost dominată pe de o parte de servlet/JSP, iar pe de altă parte de ASP. Apariţia ASP.NET introduce în competiţie un element nou. În sfârşit, PHP vine puternic din urmă. Care va fi mâine realitatea? Greu de anticipat.

326

În spiritul prezentei lucrări ne simţim obligaţi să subliniem câteva dintre calităţile oferite de servlet/JSP, calităţi care nu se regăsesc la celelalte tehnologii: • Performanţa Tehnologia servlet este superioară CGI, deoarece nu este necesară crearea unui nou proces la fiecare cerere, ci numai un thread întreţinut de containerul de servlet-uri. • Portabilitate Aplicaţiile servlet sunt portabile, deoarece atât servlet-ul, cât şi containerul de servlet sunt scrise în Java. • Dezvoltare rapidă a aplicaţiilor Fiind o thnologie Java, servlet are acces la o bibliotecă bogată de pachete specializate, sporind astfel viteza de proiectare. • Robusteţe Fiind vorba de Java, JVM controlează gestiunea memoriei, garbage collection etc. Astfel proiectantul este scutit de o serie de gestiuni care de regulă conduc la pene de program greu de depistat. • Acceptare largă Din ce în ce mai multe case de soft preiau Java ca şi principală platformă de dezvoltare a proiectelor lor. Astfel, aplicaţiile servlet/JSP se pot integra uşor în proiecte de anvergură.

7.1.2. Arhitecturile aplicaţiilor servlet
Formal vorbind, un servlet este o clasă care poate fi încărcată dinamic şi apoi rulată într-un server web de tip special, numit container de servlet-uri (servlet engine). Un servlet interacţionează cu clienţii după modelul HTTP bazat pe cerere - răspuns. Din această cauză, containerul de servlet-uri permite comunicarea prin protocolul HTTP pentru cereri de la clienţi şi răspuns al servlet-ului. În plus, în special din raţiuni de securitate [71], containerul suportă şi protocoale înrudite, ca de exemplu HTTPS (HTTP securizat prin SSL). La această oră sunt cunoscute două tipuri de arhitecturi ale aplicaţiilor servlet: • aplicaţii prin container de sine stătător (standalone); • aplicaţii cu container cascadat dintr-un server web. 327

În fig. 7.1 este prezentată arhitectura cu container standalone.
Servlet Container servleturi Raspuns HTTP Continut static

Cerere HTTP Client (browser)

Figura 7.1. Servlet cu container standalone

O aplicaţie servlet conţine, pe lângă partea dinamică condusă de servlet şi o parte statică specifică web, ca de exemplu pagini HTML şi fişiere cu imagini. Containerul de servlet având şi funcţii de server web, va trebui, pe lângă execuţia servlet-ului, să gestioneze şi aceste conţinuturi statice. Pentru aplicaţii mai serioase este de preferat ca partea statică să fie gestionată de către un server web robust şi destinat special, cum ar fi de exemplu Apache sau Microsoft IIS. Prin aceasta, containerul va fi mai puţin încărcat, iar serviciile web relative la aplicaţie vor fi mai bine şi mai sigur gestionate. Astfel apare necesitatea celei de-a doua arhitecturi, cea din fig. 7.2.
Container servleturi Server Web Raspuns HTTP Continut static

Cerere HTTP Client (browser)

Servlet

Figura 7.2. Servlet împreună cu un server web

7.1.3. Containere de servlet
7.1.3.1. Probleme generale Un container de servlet-uri este deci o aplicaţie Java cu rol de maşină virtuală în care să ruleze servlet-urile. Funcţionalitatea unui container nu se reduce numai la simpla execuţie a unui servlet, ci el trebuie să gestioneze acest servlet pe toată durata de viaţă a acestuia. La ora actuală există numeroase implementări de containere, unele dintre ele comerciale, altele dezvoltate ca proiecte open-source, care pot fi liber 328

conform Sun Microsystem. Dintre containerele open-source.1. Primul dintre ele este dezvoltat în paralel cu server-ul web Apache şi s-a impus ca model de referinţă. de obicei cu extensia . Aceste produse excelează în primul rând prin uşurinţa în instalare şi configurare.conf. Unele containere.distribuite. în cele ce urmează vom utiliza două variabile de mediu: • CATALINA_HOME prin care vom marca locul în structura de directori unde se află instalat (urmează a se instala) containerul Tomcat. Recomandăm cititorilor interesaţi parcurgerea acestor specificaţii. recomandat de Sun Microsystem. în timp ce un container pentru Windows 2000 poate fi însoţit de fişiere binare care să permită lansarea containerului ca serviciu.org/aboutJava/communityprocess/first/jsr053. pe o staţie Windows2000 această variabilă poate fi: 329 . 7. aproximativ 36 de implementări de containere. lista completă poate fi consultată la adresa: http://java. fie un director al utilizatorului. De exemplu. diferă prin fişierele script de lansare. toate implementările trebuie sa respecte specificaţiile Sun Microsystem cu privire la servlet-uri. dezvoltate pentru platformele Microsoft Windows.2. de obicei cele comerciale. Fie că sunt comerciale. Implementările open-souce prezintă opţiunile de configurare în fişiere text. Altele pot prezenta o interfaţă web-based pentru a efectua aceste operaţii. se instalează cu ajutorul unui program de setup. Instalarea şi configurarea Tomcat În continuare ne vom referi numai la containerul Tomcat. două dintre cele mai cunoscute sunt Tomcat şi Jetty. s-au impus pe piaţă Macromedia Jrun.com/products/servlet/industry. Borland AppServer şi IBM WebSphere Application Server.sun. Dintre implementările comerciale de containere. Două versiuni ale aceluiaşi container. Configurarea unui container de servlet-uri diferă de la caz la caz. au diferite componente grafice pentru administrare şi configurare. Fiind scris în Java. care sunt disponibile pe Internet la adresa: http://jcp. Pentru fixarea ideilor. sau mai noi în fişiere xml. pentru două platforme diferite. Pentru a putea rula un servlet trebuie instalat şi configurat un container de servlet-uri. fie că sunt open-source. Spre exemplu un container pentru Linux poate prezenta script-uri care să permită lansarea în execuţie a containerului respectiv într-un mod compatibil System V.html. Există în prezent. Unele containere.3. Alte containere pot fi descărcate de pe Internet într-un singur fişier arhivă. de obicei zip Instalarea acestor containere se reduce la simpla dezarhivare a acestui fişier într-un anumit director. este fie un director sistem. După caz. un container de servlet-uri este în general independent de platformă.

4. fie un director al utilizatorului.org. După caz.gz se dezarhivează acest fişier în $CATALINA_HOME.exe pentru platformele Windows. setarea unei variabile VAR se face: o Pentru Windows 9x.rpm pentru platformele Linux.exe se execută fişierul exe descărcat. Dacă s-a optat pentru formatul . pe o staţie Windows 2000 această variabilă poate fi: o C:\j2sdk1. De exemplu.0 Setarea variabilelor de mediu se face în conformitate cu cerinţele specifice ale sistemului de operare pe care se lucrează.gz pentru ambele platforme. • Instalarea containerului Tomcat În funcţie de formatul în care a fost descărcat containerul Tomcat. fie în ~/.apache.bash_profile următoarele două linii: VAR=valoare export VAR Pentru instalarea Tomcat sunt necesari următorii paşi: • Descărcarea containerului Tomcat Se face de pe Internet de la adresa http://jakarta.zip sau tar. Containerul Tomcat poate fi descărcat în mai multe formate: . acest lucru se face adăugând în fişierul autoexec. este fie un director sistem. De exemplu.bat linia: set VAR=valoare o Pentru sistemele de operare Windows NT sau Windows 2000 această variabilă se setează apelând pe rând: My Computer / click dreapta / Properties / Users Profiles / User defined Variables.o C:\jakarta-tomcat-4. . executată ca root: o rpm –Uhv $CATALINA_HOME 330 .rpm. Dacă s-a optat pentru formatul .zip sau tar.0. instalarea poate să difere.profile fie în ~/. fie în /etc/profiles. atunci containerul poate fi instalat cu următoarea comanda Linux. o Pentru Unix se depun. Daca s-a optat pentru formatul . .1 • JAVA_HOME prin care vom marca locul în structura de directori unde se află instalat pachetul Java. după caz.

• clases Este directorul cu clasele globale necesare aplicaţiilor web. Dacă se doreşte.• Configurarea containerului Mai întâi trebuie setată variabila JAVA_HOME. Setarea se face în conformitate cu cerinţele sistemului de operare gazdă. Restul elementelor de configurare le vom prezenta în secţiunile următoare.xml care este fişierul principal de configurare Tomcat şi web.bat După instalarea Tomcat. se poate seta şi variabila CATALINA_HOME.bat. Pentru platformele Unix se va rula script-ul startup. cele mai importante sunt server. se vor modifica unele dintre numerele de port cu care operează Tomcat. în $CATALINA_HOME apare următoarea structură de directori: • bin Este directorul în care se găsesc programele execuabile şi scripturile de bază Tomcat. iar pentru Windows script-ul startup. • lib Conţine o serie de clase pentru folosinţă comună. • Lansarea containerului Tomcat În $CATALINA_HOME există un subdirector bin care conţine script-urile de lansare ale lui Tomcat. • server Conţine fişierele arhivă (tip .sh %CATALINA_HOME%\bin\startup. grupate în arhive . 331 . adică: ${CATALINA_HOME}/bin/startup.xml care este fişierul machetă pentru descriptorii aplicaţiilor.jar. Dintre acestea. • conf Este directorul pentru fişierele de configurare. Dacă este cazul.jar) ale Tomcat.sh.

• logs Este directorul în care Tomcat întreţine o serie de fişiere jurnal (log-uri). Consultarea acestora este uneori utilă, mai ales când apar o serie de erori în funcţionarea unui servlet. • common Conţine o serie de clase de folosinţă atât pentru Tomcat, cât şi pentru aplicaţiile web. • webapps Este directorul în ale cărui subdirectoare pot fi găzduite diverse aplicaţii web. Aici se află, printre altele exemplele demonstrative de aplicaţii livrate odată cu distribuţia Tomcat. Tot aici pot fi plasate noi aplicaţii. Pentru fiecare aplicaţie se va depune ori structura completă de directori a ei, ori o arhivă .war a acestei structuri, care va fi desfăcută automat de către Tomcat. Odată cu distribuţia Tomcat, acest director conţine cel puţin subdirectoarele ROOT, manager şi examples. • work Este spaţiul în care sunt depuse servlet-urile generate automat din paginile JSP. Recomandăm ca în primă fază, instalarea şi configurarea unui container de servlet să o facă programatorul. Acesta trebuie să se familiarizeze cu toate opţiunile de configurare şi cu modalităţile de instalare a unui astfel de container, pentru a putea folosi şi exploata la maxim serviciile oferite. De asemenea, recomandăm ca în faza de dezvoltare şi testare a unei aplicaţii, containerul să fie de asemenea întreţinut de către programator şi repornit ori de câte ori este nevoie. Astfel, pentru testare fiecare programator îşi va întreţine propria instanţă Tomcat. În faza de exploatare se va folosi, eventual, un Tomcat unic, întreţinut şi securizat de către un administrator. 7.1.3.3. Gestiunea porturilor Un container servlet are un port de ascultare cereri HTTP. De obicei acesta este portul 8080, dar el poate diferi de la container la container. Specificarea acestui număr de port se face în fişierul de configurare $CATALINA_HOME/conf/server.xml unde se găseşte secvenţa următoare:

332

<!-- Define a non-SSL HTTP/1.1 Connector on port 8080 --> <Connector className="org.apache.catalina.connector.http.HttpConnector" port="8080" minProcessors="5" maxProcessors="75 enableLookups="true" redirectPort="8443" acceptCount="10" debug="0" connectionTimeout="60000"/>

Dacă doreşte, utilizatorul poate să modifice acest număr de port după care trebuie să relanseze Tomcat. Această modificare este absolut necesară pentru evitarea conflictelor de aşteptare la porturi, atunci când pe aceeaşi maşină mai mulţi utilizatori îşi lansează propriile containere Tomcat. Pe lângă acest port, containerul mai foloseşte încă cel puţin două porturi. Unul dintre acestea este portul de oprire Tomcat. La acest port containerul aşteaptă comanda de oprire. În fişierul de configurare $CATALINA_HOME/conf/server.xml apare:
<Server port="8005" shutdown="SHUTDOWN" debug="0">

Printr-o comandă:
TELNET adresaMaşinaTomcat 8005

se realizează conectarea la acest port, după care se tastează o linie cu conţinutul şirului specificat prin atributul shutdown (în cazul de mai sus este şirul SHUTDOWN). Astfel se poate comanda, eventual de la distanţă, oprirea Tomcat. Dacă se doreşte, se poate schimba portul sau şirul de comandă a opririi. Pe lângă aceste două porturi, Tomcat mai utilizează şi următoarele numere de port:
Număr port 8008 8443 8082 8081 8009 Rol Conexiune warp cu serverul Apache conexiune SSL HTTP/1.1 Conexiune non SSL HTTP/1.0 Conexiune serviciu proxy Conexiune AJP

Pe moment este suficient cât am prezentat despre gestiunea porturilor. Într-o secţiune următoare o să revenim pentru conexiunea cu Apache. După fixarea numerelor de porturi, practic containerul este instalat. Pentru a verifica că instalarea este bună, mai întâi se va porni containerul, după caz sub Unix sau sub Windows: ${CATALINA_HOME}/bin/startup.sh %CATALINA_HOME%\bin\startup.bat

333

Apoi se va lansa, pe maşina pe care s-a pornit containerul, http://localhost:8080, iar dacă s-a modificat portul atunci în loc de 8080 se va pune portul respectiv. Efectul trebuie să fie afişarea paginii din fig. 7.3.

Figura 7.3. Pagina de start Tomcat

Pagina cuprinde printre altele şi o documentaţie completă despre container, o documentaţie completă despre Servlet API, precum şi câteva exemple de servlet-uri însoţite de codurile lor sursă. ATENŢIE Asiguraţi-vă că navigatorul nu are setat proxy. În caz contrar, navigatorul va trimite cererea http la server-ul proxy, care va interpreta localhost ca o maşină pe care rulează server-ul proxy şi nu maşina pe care rulează containerul! 7.1.3.4. Contextul unei aplicaţii Servlet: definire şi creeare Într-o aplicaţie web pot să apară servlet-uri, clase Java, pagini JSP, documente html statice, applet-uri Java etc. Toate aceste resurse trebuie plasate într-un director, numit rădăcina aplicaţiei şi în subdirectoare ale acestuia, respectând o anumită structură. Pentru fixarea ideilor, vom defini o variabilă de 334

mediu cu numele WEBAPP_HOME. Valoarea ei va fi calea de la rădăcina sistemului de fişiere şi până la directorul rădăcină al aplicaţiei. De exemplu, WEBAPP_HOME poate avea una dintre valorile: WEBAPP=${CATALINA_HOME}/webapps/OAplicatie WEBAPP=d:/Florin/Carti/Servlet/OAplicatie În primul caz aplicaţia OAplicatie este plasată în spaţiul rezervat de Tomcat pentru aplicaţiile utilizatorilor, iar în al doilea caz ea este plasată undeva în sistemul de directori. (NB - Tomcat foloseşte separatorul de directoare /, indiferent dacă este pe Unix sau pe Windows). Din păcate (sau poate din fericire), aplicaţia web din $WEBAPP_HOME nu poate fi invocată prin specificare absolută:
http://adresamasina[:port]/$WEBAPP_HOME...

Adică nici una dintre specificările:
http://localhost:8080/C:/jakarta-tomcat-4.0.1/webapps/OAplicatie... http://localhost:8080/d:/Florin/Carti/Servlet/OAplicatie...

nu este acceptată de către Tomcat! Tomcat, ca de altfel orice container de servlet, foloseşte pentru specificări un mecanism de alias, prin intermediul conceptului de context. El poate fi privit ca un domeniu de vizibilitate, care poate fi accesat din resursele conţinute în cadrul aplicaţiei web. Fiecare aplicaţie web are un singur context, iar containerul trebuie să-i asigure acestuia securitatea necesară. De exemplu, două aplicaţii web distincte nu îşi vor putea accesa una alteia contextul. Pentru fiecare aplicaţie se va defini un context care va avea asociat un nume. Pentru fixarea ideilor, vom defini o variabilă de mediu WEBAPP_NAME care va conţine numele asociat contextului. O primă întrebare se impune: Cum se defineşte un context? Definirea contextelor şi a parametrilor de configurare a acestora se face prin intermediul tag-urilor <HOST şi <CONTEXT din fişierul $CATALINA_HOME/conf/server.xml. O a doua întrebare: Cum se leagă directorul aplicaţiei de context? O astfel de întrebare apare în mod firesc, deoarece aplicaţia se află în $WEBAPP_HOME, iar containerul (Tomcat) o vede ca $WEBAPP_NAME. Vom răspunde deodată la ambele întrebări.

335

.</CONTEXT> .... alteAtribute ...- Un tag <HOST defineşte un host virtual.</HOST> . > ... Atributul appBase defineşte prin dirAplicatii subdirectorul lui $CATALINA_HOME destinat să găzduiască aplicaţiile web.definire caracteristici context prin taguri interioare .. partea legată de definirea contextelor Tomcat este: . Distribuţia Tomcat are definit în fişierul de configurare numai un singur host virtual..... Schematic. definit prin: <HOST name="localhost" appBase="webapps" .<HOST name="adresaMasina" appBase="dirAplicatii" . ne vom opri numai la două atribute principale: name şi appBase.<CONTEXT path="/nume" docBase="caleReala" . 84]. alteAtr . iar într-un tag <HOST apar mai multe tag-uri interioare lui.. În interiorul tag-ului <HOST se definesc tag-urile <CONTEXT. > . numele contextului fiind acelaşi cu numele directorului. 336 . Mai jos prezentăm şi alte modalităţi de creare de contexte. Fără să intrăm în detalii (utilizatorul poate consulta [87] sau comentariile din fişierul de configurare).. /> sau <CONTEXT path="/nume" docBase="caleReala" ... directorul de aplicaţii indicat de acest host implicit este: $CATALINA_HOME/webapps O primă regulă relativă la crearea de contexte este aceea că toate subdirectoarele directorului de aplicaţii devin în mod automat contexte. alteAtr . > Aşa cum am arătat mai sus. Maniera de specificare în cele două tag-uri este specifică limbajului XML [87. 38...Tag-ul <HOST defineşte pentru container un host virtual. Atributul name al lui specifică prin adresaMasina adresa reală a maşinii care găzduieşte host-ul virtual. Din tag-ul <CONTEXT ne interesează numai atributele path şi docBase. Pe lângă aceste atribute mai sunt şi altele..

Prin atributul docBase se specifică caleReala din sistemul de directori unde se află aplicaţia. toate specificate relativ. Distribuţia Tomcat vine cu trei contexte predefinite.. astfel: http://adresamasinaTomcat:portTomcat/$WEBAPP_NAME/.. alteAtr . În cazul specificării relative valoarea variabilei WEBAPP_HOME se obţine concatenând trei elemente: valoarea variabilei CATALINA_HOME. 337 . Valoarea nume din atributul path al unui context este numele contextului. urmată de valoarea atributului appBase din <HOST.. /> Pentru definiri de tag-uri mai complexe. pentru o anumită aplicaţie vom avea WEBAPP_NAME=nume.. ca mai jos: • Contextul vid. adică: WEBAPP_HOME=caleReala Referirea la aplicaţie se face prin intermediul contextului. adică: WEBAPP_HOME=$CATALINA_HOME/dirAplicatii/caleReala În cazul specificării absolute. > . destinat gestiunii Tomcat. alteAtribute .. implicit când nu se specifică nici un context. • Contextul manager.. se foloseşte o structură XML cu tag de început şi de sfârşit: <CONTEXT path="/nume" docBase="caleReala" . valoarea variabilei WEBAPP_HOME este valoarea atributului docBase din <CONTEXT...</CONTEXT> Şi acum răspunsul la ambele întrebări. În continuarea URL-ului se pot specifica resurse din orice subdirectoare ale contextului.. Deci. definit prin <Context path="/manager" docBase="manager" . atunci se foloseşta un tag vid (în terminologia XML [84]): <CONTEXT path="/nume" docBase="caleReala" ... El se defineşte prin <CONTEXT path="" docBase="ROOT . Specificarea caleReala poate fi făcută relativ la appBase sau absolut. Exemple de creare a unor contexte: 1..Din interiorul unui tag <HOST ne interesează doar tag-urile <CONTEXT cu ajutorul cărora se pot defini contexte pentru diferite aplicaţii.. Dacă pentru definirea unui context sunt suficiente atributele din cadrul tag-ului.. încheiată cu valoarea atributului docBase din <CONTEXT..

referirea de pe maşina locală prin portul 8080 la o resursă din contextul examples. găzduit în directorul standard de aplicaţii. Se doreşte crearea unui context cu numele OAplicatie. spre exemplu de pe maşina locală. Tomcat va avea grijă ca la prima încărcare a lui să desfacă automat arhiva în webapps/OAplicatie şi să creeze contextul cu numele OAplicatie. /> 4.. Cu notaţiile de mai sus vom avea EBAPP_HOME=d:/Florin/Carti şi WEBAPP_NAME=TfJaW Referirea la o resursă din acest context. Apoi. De exemplu.130:8080/TfJaW/...zip. contextul se defineşte prin: <Context path="/TfJdW" docBase="d:/Florin/Carti" . Pentru aceasta. Apoi se creează din întreaga structură o arhivă zip cu numele OAplicatie. 2. 338 .. În sfârşit. care conţine în el mai multe exemple demonstrative de lucru cu servlet şi cu JSP. referirea este: http://193.40. spre exemplu de pe maşina locală. Se doreşte. se face prin: http://localhost:8080/TfJaW/.. Pentru aceasta este suficient să se creeze. numai că structura de directoare şi fişiere pentru aplicaţie poate fi pregătită în altă parte. referirea.war şi această arhivă se va depune în $CATALINA_HOME/webapps..• Contextul examples. se face prin: http://localhost:8080/OAplicatie/. în $CATALINA_HOME/webapps directorul OAplicatie care să conţină structura de directori şi fişierele necesare aplicaţiei. la o resursă din contextul OAplicatie. se face prin: http://localhost:8080/examples/. munele acesteia va fi schimbat în OAplicatie. prin adresa IP... pe o maşină Windows.. definit prin <Context path="/examples" docBase="examples" .. aşa cum pretinde Tomcat.. crearea unui context cu numele TfJaW (acrostih de la titlul prezentei cărţi) care să se refere la o aplicaţie găzduită în subdirectorul Carti al directorului Florin din rădăcina discului D:. De pe o altă maşină. La fel ca şi la punctul 2. 3..226.

Dacă aplicaţia web este conţinută în alt loc în sistemul de fişiere. este următoarea: $WEBAPP_HOME $WEBAPP_HOME/WEB-INF/web.5. 7. iar locul fizic este indicat prin atributul docBase al tag-ului.Rezumând problema contextului.xml $WEBAPP_HOME/WEB-INF/classes/ $WEBAPP_HOME/WEB-INF/lib/ • În directorul $WEBAPP_HOME se plasează resursele statice şi paginile JSP ale aplicaţiei. O anumită resursă dintr-o aplicaţie este găzduită în interiorul unui context deja definit. al cărui nume este dat de atributul path al tag-ului <CONTEXT. 3. Tomcat va crea automat un subdirector pentru aplicaţia respectivă. referirea la resursă se face din contextul gazdă completat cu calea relativă până la resursă. 4. există patru posibilităţi prin care se leagă spaţiul fizic al unei aplicaţii WEBAPP_HOME. 339 . Structura de subdirectoare a unui context şi invocarea de resurse Structura de subdirectoare şi fişiere a unui context.war cu numele aplicaţiei. 2. caz în care contextul este creat automat. cu numele de context WEBAPP_NAME pentru acces la container: 1. În $CATALINA_HOME/webapps se depune o arhivă nume. iar numele contextului este chiar numele directorului respectiv.1. în conformitate cu specificaţiile Sun. În $CATALINA_HOME/webapps se creează un subdirector pentru aplicaţia respectivă.xml se numeşte descriptor al aplicaţiei şi prin intermediul lui se pot fixa o serie de configurări pentru aplicaţia web respectivă: parametri de iniţialzare. În interiorul acestuia apar câteva fişiere şi directoare. • Fişierul $WEBAPP_HOME/WEB-INF/web. • Directorul $WEBAPP_HOME/WEB-INF/ este folosit în aplicaţie numai prin intermediul containerului. atunci se defineşte un context nou.3. În acest caz. iar numele contextului va fi chiar numele directorului respectiv. Contextul unei aplicaţii devine operaţional odată cu pornirea containerului după configurare.

Accesul la acest servlet se face folosind un alias intermediar care substituie construcţia WEBINF/classes. driver-ele JDBC etc. am definit un context numit pdpj (acrostih după Programare Distribuită pe Platforme 340 . prin: http://localhost:8080/examples/servlet/SessionExample Pentru multe dintre exemplele care urmează în acest capitol. acesta va fi depus în $WEBAPP_HOME/WEBINF/classes/. În multe aplicaţii acest fişier coincide cu cel implicit livrat de distribuţia Tomcat. De exemplu poate plasa imaginile aplicaţiei într-un director numit $WEBAPP_HOME/images/. spre exemplu. servlet-ul SessionExample. arhivele JAR. • Directorul $WEBAPP_HOME/WEB-INF/lib/ conţine clasele adiţionale. Structura de directoare a unui aplicaţii web nu este rigidă.jpg Accesarea unui servlet Aşa cum am precizat mai sus. acesta poate fi invocat. Programatorul poate să extindă această structură de directoare după cum doreşte. • Directorul $WEBAPP_HOME/WEB-INF/classes/ conţine fişierele de tip . repornit containerul. de pe maşina locală. Accesul la o resursă statică Accesul la o resursă statică a aplicaţiei web poate fi invocată prin container folosind un URL de forma: http://masinaTomcat:port/$WEBAPP_NAME/numeResursa unde în loc de $WEBAPP_NAME apare numele contextului aplicaţiei. după care referirea se poate face prin: http://masinaTomcat:port/$WEBAPP_NAME/images/poza.class. Presupunând că avem un servlet ClasaServlet. după care poate fi invocat prin: http://masinaTomcat:port/$WEBAPP_NAME/servlet/ClasaServlet Pentru a lansa.class pentru toate servlet-urile şi bean-urile folosite de aplicaţie. necesare rulării aplicaţiei web.class livrat odată cu distribuţia Tomcat. un servlet trebuie plasat în directorul $WEBAPP_HOME/WEB-INF/classes/.elemente de depanare etc.

Figura 7. practica a demonstrat că Tomcat-Standalone nu face faţă rezonabil. 7. Fereastra de pornire Tomcat Serviciul Tomcat-Standalone oferă containerului funcţionalităţi relativ reduse şi puţin performante ca şi server web. 7. se recomandă înainte salvarea fişierului de configurare într-o copie de siguranţă. Containerul Tomcat. Tomcat conectat la Apache Containerul Tomcat are definite două servicii. Serviciul TomcatStandalone funcţionează aşa cum am arătat în fig.6.. De aceea se recomandă ca el să se ocupe doar de gestiunea servlet-urilor şi să fie degrevat total de partea statică (vezi fig. în interiorul acestuia am definit subdirectorul WEB-INF iar în acesta am creat directoarele classes. în care pot să apară mulţi clienţi.1.xml. este suficient să se şteargă din fişierul de configurare liniile dintre <Service name="Tomcat-Apache"> şi </Service>.4. Mai întâi am creat directorul $CATALINA/webapps/pdpj.2). Serviciul Tomcat-Apache funcţionează aşa cum am arătat în fig.. ca la nevoie să poată fi refăcut. Cele două moduri de lucru sunt descrise în fişierul de configurare $CATALINA_HOME/conf/server.4.xml din $CATALINA_HOME/ROOT/web.. Pentru a reduce consumul de resurse Tomcat. lib şi am copiat fişierul web. Evident. Pentru aplicaţii de anvergură.-</SERVICE> <SERVICE name="Tomcat-Apache"> . ca de exemplu Apache. El este recomandat mai ales în faza de testare a unei aplicaţii.1 şi 7. utilizatorul poate dezactiva complet serviciul Tomcat-Apache. Pentru aceasta.Java).3. Sarcinile de natură statică vor fi transmise unui server web profesional. 7. acesta anunţă pornirea celor două servicii ca în fig. 7.1. 7. În acest scop se foloseşte serviciului oferit de Tomcat-Apache.2.xml prin intermediul tag-urilor <SERVICE: <SERVICE name="Tomcat-Standalone"> .</SERVICE> La pornirea Tomcat. integrat împreună cu 341 .

sunt necesare mici configurări atât la Tomcat.2). numărul de port (8008) va fi schimbat în cele două fişiere de configurare. appBase="webapps" .. există un modul adiţional numit mod_webapp./> . trebuie dată câte o linie WebAppDeploy. La nivel de server web.catalina. atunci cererea va fi deservită direct de Apache.<Connector className="org. trebuie dată câte o linie WebAppConnection. Pentru integrarea Tomcat cu Apache. 7. Acesta o procesează. Dacă cererea invocă containerul. există o singură linie LoadModule... cu nume de conexiuni diferite. Urmează câte o line WebAppDeploy pentru fiecare aplicaţie web conectabilă la Tomcat.so WebAppConnection numeConexiune masinaTomcat:8008 WebAppDeploy $WEBAPP_NAME numeConexiune /$WEBAPP_NAME/ Din punct de vedere formal.. Dacă este cazul. Să numin $WEBAPP_NAME numele contextului aplicaţiei. atunci server-ul web trimite cererea containerului. Server-ul web caută în URL apariţia substring-ului "/$WEBAPP_NAME/". Fiecare linie WebAppConnection defineşte o conexiune Warp (numeConexiune) la o anumită maşină Tomcat.apache.</Service> În fişierul de configurare httpd. Pentru partea Tomcat. Pentru fiecare maşină Tomcat la care se doreşte conexiune. când Apache primeşte din exterior o cerere pentru o resursă care nu solicită containerul Tomcat (vezi fig... pentru ca acesta să poată comunica cu conectorul Warp: LoadModule webapp_module libexec/mod_webapp. Pentru fiecare aplicaţie conectabilă la un Tomcat.server-ul Apache oferă performanţe mai bune pentru aplicaţii şi oferă aplicaţiilor web un mediu mai sigur de rulare.WarpConnector" port="8008". Prin intermediul primei linii se comandă serverului web să încarce în memorie modulul adiţional corespunzător. cât şi la Apache. În interiorul acestui tag se defineşte conectorul Warp. care la rândul lui defineşte un port pe care ascultă cereri de la server-ul web. cererea este trimisă la Tomcat spre a fi 342 . Partea aceasta din configurare este: <Service name="Tomcat-Apache"> .conf al server-ului Apache trebuie adăugate câteva linii. este suficient să se păstreze în fişierul de configurare liniile prin care se defineşte serviciul Tomcat-Apache..warp.connector. iar acesta la rândul lui răspunde clientului.dll (cazul Microsoft IIS). dă răspunsul către Apache. de comunicare cu Tomcat. În caz că în URL apare acest substring imediat după specificarea maşinii şi a portului.so (cazul Apache) sau mod_webapp. Astfel.

Funcţionarea unui servlet 7. IOException { . un servlet reprezintă în programarea web o alternativă la CGI. toate exemplele noastre folosesc clasa HttpServlet pentru proiectarea servlet-urilor.4.*.executată.- 343 .*.getInputStream() la cerere binara . HttpServletResponse raspuns) throws ServletException. primirea unei cereri şi întoarcerea răspunsului. atunci cererea este executată de către Apache. import java.// Obtinerea elementelor cererii. Formal. 7. marea majoritate a servlet-urilor extind subclasa HttpServlet a clasei Servlet.servlet.getParameter(. Cum arată un servlet? Aşa cum am mai spus. Practic. import javax.io. public class UnServlet extends HttpServlet { public void doGet ( // Metoda GET // public void doPost( // Metoda POST HttpServletRequest cerere..getOutputStream() pentru raspuns binar . Din punct de vedere practic..4. Selectarea containerului Tomcat se face prin numeConexiune.1. ea fiind aliniată complet la protocolul HTTP. Asta înseamnă că trebuie să preia o cerere HTTP printr-una din metodele GET.. Fragmentul de program sursă de mai jos prezintă schema cadru a părţii esenţiale a unui servlet.// Tratarea cererii si pregatirea raspunsului .. rezidentă în directorul lui de aplicaţii webapps (indicată prin atributul appBase în <Connector). Aplicaţia pe care o va rula acest Tomcat va fi $WEBAPP_NAME.1..// Obtinerea unui obiect pentru trimiterea raspunsului: // raspuns.. un servlet este o clasă care extinde clasa Servlet şi utilizează metoda service a acestei clase pentru tratarea cererii şi a răspunsului.) la cerere text // cerere. Dacă string-ul "/$WEBAPP_NAME/" nu apare. POST etc.servlet.getWriter() pentru raspuns text // raspuns. eventual prin: // cerere. import javax.http. Apoi trebuie să întoarcă un răspuns cu respectarea protocolului HTTP.*.1.

care procesează cererile clienţilor.. opţională şi ea. Ea primeşte la intrare numele câmpului şi dă la ieşire valoarea acestuia. În secţiunile următoare vom relua şi detalia elementele constitutive ale unui servlet. Accesarea datelor transmise prin cererea clientului Pentru accesarea datelor transmise prin cererea trimisă de client. Metodele din clasa HttpServlet. De cele mai multe ori. metoda service transmite cererea spre procesare. cum ar fi getParameterNames pentru obţinerea numelor câmpurilor.4. obiectul cerere (de tip HttpServletRequest) permite obţinerea elementelor constitutive ale cererii. 24. în fapt nu întotdeauna prezentă. 51]. sau getParameterValues pentrua obţine toţi parametrii. unor metode precum doGet sau doPost.doGet } // UnServlet Lipseşte de aici partea de iniţializare.2. 7.// Fixarea si trimiterea unor parametri din antetul raspunsului // Creearea si trimiterea corpului raspunsului . au două argumente: • un obiect HttpServletRequest care încapsulează datele transmise de la client. • un obiect HttpServletResponse care încapsulează răspunsul server-ului spre clienţi. valorile câmpurilor se obţin cu ajutorul metodei getParameter. Comunicarea este făcută cu respectarea protocolului HTTP [86. De asemenea.. Interacţiunea cu clienţii Un servlet gestionează cererile clienţilor cu ajutorul metodei service.. String-ul de cerere nu trebuie parsat. În funcţie de metoda (GET sau POST) cu care a fost formulată cererea clientului. ca şi partea de distrugere a servlet-ului. La nevoie mai pot fi folosite şi alte metode ale obiectului cerere. în cazul metodei GET.// Inchiderea obiectelor } // UnServlet. cererea este dată printr-un string de cerere conţinând nume de câmpuri şi valori ale acestora. Acest string de cerere poate sosi fie ca opţiune adăugată la URL-ul cererii. fie ca şi corp al cererii în cadrul metodei POST. sub forma: nume1=valoare1&nume2=valoare2. 344 .1.

înglobează conform protocolului HTTP. Tot metoda POST permite. obiectul HttpServletResponse oferă unul dintre următoarele două moduri de a transmite date: • folosind metoda getWriter. Transmiterea răspunsului Obiectul răspuns (de tip HttpServletResponse parametru al metodei doGet sau doPost). care întoarce un obiect ServletOutputStream. Sursa acestuia este dată în programul 7. public class Primul extends HttpServlet { public void doGet(HttpServletRequest cerere.*.*. pentru a scrie date de tip text.io.4. metoda setContentType setează tipul conţinutului răspunsului. Mai întâi. import java.1) vom presupune pentru primul servlet că ar putea eventual primi şi un string de cerere. 7. de tip BufferedReader. care întoarce un obiect Writer. Pentru accesarea acestuia. Pentru accesarea acestora.1. • folosind metoda getOutputStream. obiectul cerere permite obţinerea unui obiect IO de acces. Pentru a fi în ton cu schema cadru (7. ca alternativă la codul binar.http. obţinut de la metoda getInputStream. obiectul cerere permite obţinerea unui obiect IO de acces.există mai multe metode prin care se pot accesa valori din antetul (header-ul) cererii. Pentru trimiterea corpului răspunsului.1. servlet-ul fixează header-ul HTTP al mesajului de răspuns. import javax.3. să aibă în corp linii text. De exemplu.*. răspunsul către client.4. obţinut de la metoda getReader. 345 . În cazul metodei POST este posibil ca în corpul cererii să fie cod binar. Primul exemplu şi compilarea unui servlet Cel mai simplu servlet este unul de tip HelloWorld.1. care nu primeşte nici o cerere şi dă ca răspuns un text static. de tip ServletInputStream. import javax.servlet.servlet. pentru a scrie date binare.

spre exemplu. out.close(). În cazul Tomcat. dar se poate adapta uşor la Unix. 7.println("<HTML>"). am creat un director cu numele test în %CATALINA_HOME%\webapps. compilatorul trebuie să poată accesa arhiva servlet. Imaginea afişată de navigator va fi cea din fig. } // Primul.println("Am primit: "+cerere.5. această arhivă se află în $CATALINA_HOME/common/lib. Primul. Acesta va fi contextul pentru câteva dintre servlet-urile care urmează.c:\%CATALINA_HOME%\common\lib\servlet.println("</HEAD>"). ce poate folosi. out.class al servlet-ului se depune în: %CATALINA_HOME%\webapps\test\WEB-INF\classes După restartarea Tomcat. fişierul Primul. împreună cu structura descrisă în 7.setContentType("text/html"). • să compileze cu opţiunea -classpath.getWriter().jar javac -classpath "%CPATH%" *. 346 .println("<BODY>").println("<TITLE>Primul servlet</TITLE>"). Pentru exemplele (simple) care urmează. out.HttpServletResponse raspuns) throws ServletException. servlet-ul se poate apela sub diverse forme. Mai întâi vom apela: http://localhost:8080/test/servlet/Primul. pentru situaţii ocazionale. eventual prin intermediul unui fişier de comenzi.println("<CENTER><H1>Salutare!</H1></CENTER>").3. • să copieze arhiva într-un subdirector vizibil prin CLASSPATH. Utilizatorul poate: • să seteze permanent variabila de mediu CLASSPATH ca să includă această arhivă. out. După compilare.println("<HEAD>").java Pentru a putea compila un servlet.1. IOException { raspuns.. out.println("</HTML>").println("</BODY>"). out.1.java El este scris pentru platforma Windows. out.getQueryString()). out. PrintWriter out = raspuns.doGet } // Primul Programul 7.jar.5. out. următorul fişier de comenzi: set CPATH=. out.

Figura 7.6.5.Figura 7. Apel servlet Primul cu text/plain 347 . Figura 7.7. navigatorul va interpreta răspunsul ca un text simplu şi va afişa imaginea din fig. Imaginea la client va fi cea din fig. Al doilea apel al servlet-ului Primul Să înlocuim în sursa servlet-ului Primul (programul 7.1) "text/html" cu "text/plain". Prin aceasta. 7. Apel al servlet-ului Primul Apelăm apoi servlet-ul de pe o altă maşină şi în plus îi transmitem şi ceva parametri.6.7. 7. unde se poate vedea şi URL-ul de apel.

va apare imaginea cu salutul servlet-ului Primul.2 este un exemplu de apel al acestui servlet. Figura 7.2. 2.ro/florin/servlet/Primul> <input type=submit value= "Apelul servletului Primul"> </form> </body> </html> Programul 7.html Depunem fişierul Primul. 7.4. Containerul creează o instanţă a servlet-ului. după care apare imaginea din fig. 348 . 7.ubbcluj. distingem cinci momente esenţiale: 1.cs. Documentul HTML din programul 7. Containerul apelează metoda init a instanţei. Interacţiunea cu containerul Relativ la ciclul de viaţă al unui servlet în cadrul containerului.html în %CATALINA_HOME%\webapps\test\ relansăm Tomcat. Programul servlet se poate lansa şi dintr-un formular.8.4.Acelaşi servlet se poate apela şi prin intermediul unui formular. Să creăm fişierul: <html> <head> <title>Inainte de servlet</title> </head> <body> <form method=get action=http://florin.1.8. Primul. Apel Primul prin formular După ce se dă click pe butonul "Apelul servletului Primul".

containerul creează un thread nou (se alimentează dintr-un pool intern de thread-uri) şi din acesta apelează metoda service a instanţei servlet-ului pentru tratarea cererii. de exemplu la cererea administratorului server-ului. el apelează metoda init. containerul aşteaptă. Când containerul încarcă un servlet. pentru a realiza diferite operaţii de iniţializare specifice acestuia. La terminarea ciclului de viaţă a unui servlet. Înainte de distrugerea instanţei. după care aşteaptă terminarea lor. se pot folosi următoarele tehnici: • Server-ul menţine un contor care indică câte thread-uri rulează metoda service în mod curent. în servlet trebuie prevăzute secvenţe de sincronizare. pentru închiderea unor fişiere etc. • Un thread care procesează timp mai îndelungat verifică periodic dacă server-ul a primit o cerere de shutdown. Există posibilitatea de procesare a unei singure cereri la un moment dat. Această metodă este oferită de clasa HttpServlet şi poate fi suprascrisă în servlet-ul curent. La apariţia unei cereri. 349 . 4. Un servlet poate fi reîncărcat numai după ce a fost distrus de către destroy. În cazul în care procesarea cererilor accesează o resursă partajată. pentru eliberarea unor resurse. Metoda destroy este apelată când acest contor ajunge la valoarea 0. care nu presupune descrierea unor metode suplimentare. servlet-ul trebuie să implementeze interfaţa SingleThreadModel. Servlet-urile sunt capabile să deservească simultan mai mulţi clienţi. Înainte de apelul lui destroy. Iniţializarea se termină înainte ca servlet-ul să răspundă la cererile clienţilor şi înainte ca servlet-ul să fie distrus. În cazul unor cereri care necesită un timp mai mare de procesare. • Metoda destroy anunţă thread-urile care procesează aceste cereri că trebuie să-şi încheie activitatea. ca toate instanţele ce deservesc cereri ale clienţilor. containerul apelează metoda destroy. deoarece fiecare cerere este tratată într-un thread separat. Într-o secţiune viitoare vom reveni asupra iniţializărilor unui servlet. se apelează metoda destroy. eventual un interval maxim de timp.3. Instanţa este distrusă şi pusă la dispoziţia garbage collection. Această funcţie este oferită de clasa HttpServlet şi poate fi suprascrisă în servlet-ul curent. 5. şi în caz afirmativ îşi încheie activitatea. să se încheie. Pentru aceasta.

este foarte simplă. Acest neajuns poate fi evitat dacă aceste specificări absolute sunt transmise la iniţializare ca şi parametri de configurare a aplicaţiei. 2. citarea oricărui fişier din acest director se poate face prin: cale+"numeFisier" 3. deoarece este normal ca fişierele locale ale aplicaţiei să se afle în interiorul contextului. dar în cazul unui container utilizat de către mai mulţi useri pot să apară dificultăţi legate de suprapunere de nume. destinat special fişierelor locale ale aplicaţiei. De exemplu. cale = cale+File. 3. adică prin indicarea întregii căi de la rădăcină şi până la fişier. după care să citeze fişierele prefixate cu această cale. acces în orice loc al sistemului de fişiere prin specificare absolută. acces la directorul implicit al containerului. sunt căutate în acest director. Metoda este foarte bună. Accesul unui servlet la resurse locale se face prin intermediul containerului. Din păcate. 1. Pentru a obţine valoarea unui anumit parametru se apelează metoda getInitParameter. de lipsa unor drepturi de acces sau de prea multe drepturi de acces etc. Directorul implicit al containerului este $CATALINA_HOME/bin. la orice mutare a unui fişier dintre cele folosite de aplicaţie trebuie să se intervină în textul sursă. 350 . iar pentru accesul la ele este indicată una dintre următoarele trei metode: 1.Dacă pentru servlet au fost specificaţi parametri de iniţializare. Este metoda cea mai recomandată. După aceea. să presupunem că am creat în contextul aplicaţiei un director $WEBAPP_HOME/FisiereLocale/. Accesul la un fişier prin specificare absolută. 2. accesul la directorul implicit al aplicaţiei.separatorChar+ "FisiereLocale"+File. sau printr-o cale relativă. Toate fişierele invocate de servlet prin numele lor. Cele mai importante resurse locale sunt fişierele. În faza de iniţializare servlet-ul fixează calea absolută spre aceste fişiere astfel: String cale = getServletContext(). având ca argument numele parametrului.separatorChar. Cel mai simplu este să se depună fişierele în interiorul contextului aplicaţiei.getRealPath(""). servlet-ul să îşi determine mai întâi calea reală până la context. numele acestora poate fi obţinut apelând în funcţia init metoda getParameterNames().

În el sunt trecute.5. Dacă nu se introduce nici o informaţie de configurare.dtd"> <web-app> </web-app> Acest fişier se găseşte în distribuţie la: $CATALINA_HOME/webapps/ROOT/WEB-INF În funcţie de necesităţile de configurare. Configurarea şi iniţializarea servlet-urilor.7. atunci în loc de perechea <web-app> </web-app> se poate scrie un tag XML vid: <web-app/> În cele ce urmează.3//EN" "http://java. iar al doilea va mai avea şi numele CuInit. În cele ce urmează vom prezenta doar câteva elemente de configurare şi iniţializare.1 şi servlet-ul care are sursa ServletCuInit.0" encoding="ISO-8859-1"?> <!DOCTYPE web-app PUBLIC "-//Sun Microsystems. de specificare a utilizării unor filtre.1.//DTD Web Application 2. Primul va avea în plus numele First şi Unu. Trebuie să precizăm din capul locului că la crearea structurii de directori a contextului (7. Unul este servlet-ul Prim descris mai sus în programul 7. între tag-urile <web-app> şi </web-app> vor fi introduse construcţiile XML necesare.xml. Exemple Am văzut cum se face configurarea containerului prin modificarea fişierului lui de configurare: $CATALINA_HOME\conf\server. de specificare a unor elemente de securizare etc.jsp din cadrul aplicaţiei. ca servlet-ul CuInit să preia un parametru de iniţializare numit MAXIM cu valoarea 12.xml care se depune în structură se recomandă să fie unul fără conţinut semnificativ.xml Acest fişier mai poartă numele de "Deployment Descriptor" al aplicaţiei.5) fişierul web. Ne propunem. În mod analog.com/dtd/web-app_2_3.4. presupunem că avem. de asemenea.4.java ce se poate vedea mai jos în programul 7.3. Inc. are un fişier de configurare: $WEBAPP_HOME/WEB-INF/web. Este vorba de o serie de parametri de iniţializare furnizaţi prin perechi nume . Un astfel de fişier poate fi preluat din distribuţia Tomcat şi el are conţinutul: <?xml version="1. folosind standardul XML. în contextul test două servlet-uri. o serie de informaţii specifice pentru servlet-urile sau fişierele . ci lăsăm cititorul să consulte [87. Nu vom intra în detalii. Ne propunem ca prin configurare să asociem nişte nume aliate celor două servlet-uri.valoare. Fişierul de configurare pentru exemplul nostru va fi: 351 . Fişierele class ale celor două servlet-uri rămân cele iniţiale. 51] pentru amănunte.sun. fiecare aplicaţie NU fiecare servlet.1.

io.xml Conţinutul lui este prezentat în programul 7.3.getInitParameter("MAXIM")).out. import javax. int numarMaxim.sun.parseInt(config. Considerăm că fişierul are un conţinut foarte sugestiv.servlet. HttpServletResponse raspuns) 352 .*.3. În programul 7.xml (aplicaţia test) Toate tag-urile XML din acest fişier au semnificaţie precisă pentru Tomcat. import javax.*.*. import java. Informaţiile concrete de configurare sunt date ca şi conţinuturi ale celor mai interioare tag-uri.4 prezentăm sursa ServletCuInit.com/dtd/web-app_2_3. Inc.3//EN" "http://java.0" encoding="ISO-8859-1"?> <!DOCTYPE web-app PUBLIC "-//Sun Microsystems.println("Initializare. } // ServletCuInit. web.java.servlet.init public void doGet(HttpServletRequest cerere.dtd"> <web-app> <servlet> <servlet-name>First</servlet-name> <servlet-class>Primul</servlet-class> </servlet> <servlet> <servlet-name>Unu</servlet-name> <servlet-class>Primul</servlet-class> </servlet> <servlet> <servlet-name>CuInit</servlet-name> <servlet-class>ServletCuInit</servlet-class> <init-param> <param-name>MAXIM</param-name> <param-value>12</param-value> </init-param> </servlet> </web-app> Programul 7. <?xml version="1. public class ServletCuInit extends HttpServlet { int numarApeluri=0.%CATALINA_HOME%\webapps\test\WEB-INF\web.//DTD Web Application 2.http. Numarul maxim de "+ "afisari la Tomcat este: "+numarMaxim). System. motiv pentru care nu îl mai comentăm. public void init(ServletConfig config) { numarMaxim = Integer.

ServletCuInit. Afişările pe această consolă sunt prezentate în fig. out.doGet } // SevletCuInit Programul 7.getWriter().println("<TITLE>Primul servlet</TITLE>").out.println("<BODY>"). out. IOException { raspuns.out. destroy().setContentType("text/html").java Acest servlet este obţinut prin adaptarea programului 7. liniile de apel. Fereastra Tomcat la apelurile CuInit După liniile de pornire a Tomcat.println("</HTML>"). 353 .println("Am primit: "+cerere.println("<CENTER><H1>Salutare!</H1></CENTER>"). out.println într-o aplicaţie web! De fapt. out. out.println("<HEAD>"). nu se mai afişează linii pe consola Tomcat.println("Apelul nr: "+numarApeluri).getQueryString()). PrintWriter out = raspuns.throws ServletException.println("<HTML>"). Începând cu cel de-al 13-lea apel.4. out. la primul apel al servlet-ului CuInit se va afişa linia de iniţializare şi linia cu apelul nr: 1. if (numarApeluri <=numarMaxim) System. până la 12 inclusiv. În apelurile următoare se vor afişa.9. 7. aşa după cum prevede programul. toate aceste tipăriri sunt efectuate pe consola de lansare Tomcat. Cititorul poate fi surprins de prezenţa unor tipăriri System.close().println("</BODY>").9. out. numarApeluri++.1. Scopul lui este de a demonstra folosirea metodei init. } // ServletCuInit.println("</HEAD>"). out. out. Figura 7. Acest exemplu arată că metoda init este apelată doar o singură dată. out.

deci aplicaţiile pot fi transportate fără probleme între platforme. Avantajele Servlet faţă de CGI Utilizarea tehnologiei servlet este mai avantajoasă decât utilizarea CGIurilor clasice. se pot folosi atât numele atribuite prin configurare.1. cel puţin din următoarele motive: Eficienţa: Pentru fiecare apel de CGI. Este evident că servlet-urile sunt mult mai uşor de proiectat decât CGI. evident.9 se obţine doar dacă în URL-ul de apel este de forma: .1.1. JVM creează doar un nou thread. sunt produse free.5.-/servlet/CuInit Dacă în locul lui se apelează: .. cu tot ce ţine de ea.. toate containerele sunt perfect compatibile între ele. cât şi numele de clasă ale servlet-urilor. Servlet-urile Java pot să comunice uşor cu servlet-ul Web. înseamnă consum mult mai mic de resurse. 354 . În cazul apelurilor de servlet.5. Dezvoltarea unui CGI în Perl sau C reclamă respectarea unui număr mare de restricţii. deoarece metoda getInitParameter nu va găsi parametrul MAXIM! 7. ceea ce. În cazul servlet-urilor. În schimb. acestea „fac” întreaga regie în locul programatorului. server-ul Web lansează un nou proces. cu o observaţie: Comportarea din fig. Uşurinţa proiectării: Putere: Portabilitate: Java este un limbaj perfect portabil. sunt pe bază de licenţă etc. inclusiv Apache.Din cauza schimbărilor de nume din această aplicaţie. În consecinţă. Servlet vis-a-vis cu CGI 7. Soluţii ieftine: Java. Parametrii de cerere şi răspuns din servlet-uri oferă numeroase posibilităţi de colaborare cu serverul Web.-/servlet/ServletCuInit atunci Tomcat va arunca o excepţie în prima linie a metodei init. în timp ce CGI-urile nu prea. 7. platformele tradiţionale.

poate discuta request.getParameter Adresa IP a clientului Adresa Internet a clientului request. Variabilă CGI AUTH_TYPE CONTENT_LENGTH CONTENT_TYPE DOCUMENT_ROOT HTTP_XXX_YYY Semnificaţie Acces din doGet sau doPost Furnizează tipul de autorizare. Calea spre servlet request.getMethod() HEAD. Ca mai sus.5. doGet sau doPost din servlet. clasele de definire şi utilizare a servlet-urilor deţin instrumentele necesare accesului la toate variabilele de mediu oferite CGI-urilor. Deoarece servlet-ul.getAuthType() dacă există Numai pentru cereri POST. TRACE. nu este request. Rar este necesară parsarea şirului.getServletPath() REQUEST_METHOD SCRIPT_NAME 355 . În tabelul următor am presupus că request este parametrul cerere pentru service. Acest tabel dă funcţiile şi corespondenţele necesare accesului la aceste variabile. uzual GET sau POST. dă request. request. nu este necesară tratarea separată a acestei informaţii Map-area reală a unei informaţii PATH_TRANSLATED path.getServletContext().getHeader( "Xxx-Yyy") PATH_INFO Informaţia path ataşată unui URL. corespun. dacă se foloseşte request. OPTIONS.getPathTranslated() necesară la servlet Pentru cereri GET. request.1.7.getContentLength() numărul de octeţi trimişi Tipul MIME ataşat datelor request. spre deosebire de CGI. PUT.2. întoarce şirul cerere codificat de client.getRemoteAddr() request. dar ocazional request.getContentType() Calea spre director. DELETE.getRemoteHost() QUERY_STRING REMOTE_ADDR REMOTE_HOST REMOTE_USER Partea user. Acces la variabilele de mediu CGI Pentru păstrarea completă a compatibilităţii cu CGI. zătoare lui http://host/ getRealPath("/") Acces la un antet oarecare HTTP request.getPathInfo() cu server-ul web.getRemoteUser() autorizarea Intoarce metoda de cerere.getQueryString() deoarece cererea are metoda request.

getServletPath() }. request. request. { "REMOTE_ADDR". { "SCRIPT_NAME".servlet.getPathTranslated() }. { "REMOTE_HOST". { "DOCUMENT_ROOT".5 afişează conţinutul variabilelor CGI dintr-un servlet. javax.getProtocol() ( HTTP/1. String. public class ShowCGIVariables extends HttpServlet { public void doGet(HttpServletRequest request.setContentType("text/html").getQueryString() }.getRemoteUser() }. { "SERVER_NAME".getPathInfo() }.getServerName() }. getServerInfo() Programul 7. { "SERVER_PROTOCOL". request.Variabilă CGI SERVER_NAME SERVER_PORT Semnificaţie Numele server-ului Web Portul la care aşteaptă server-ul Acces din doGet sau doPost request. { "PATH_TRANSLATED". import import import import java. { "CONTENT_TYPE". request.*.getRemoteAddr() }.valueOf(request. 356 .getMethod() }.getServerPort() Name şi versiunea protocolului SERVER_PROTOCOL de cerere request.getContentLength()) }. String[][] variables = { { "AUTH_TYPE". request.*.*. out. SERVER_SOFTWARE Informaţii despre server-ul Web getServletContext(). HttpServletResponse response) throws ServletException.util.http. request.getProtocol() }. getServletContext(). { "SERVER_SOFTWARE". request.0 sau HTTP/1.getServerInfo() } }.getContentType() }.println("<html><head><title>"+ "Variabilele CGI</title></head>" + "<BODY>\n" + "<H1 ALIGN=CENTER>Variabilele CGI</H1>\n" + "<TABLE BORDER=1 ALIGN=CENTER>\n" + "<TR>\n" + "<TH>Variabla CGI<TH>Valoare"). { "QUERY_STRING".valueOf( request. String. { "SERVER_PORT". java. request. javax.1).getAuthType() }. { "REMOTE_USER". { "PATH_INFO".getWriter().servlet. request. { "REQUEST_METHOD". PrintWriter out = response. getServletContext(). request.io.getServerPort()) }.getRemoteHost() }. request.*. IOException { response.getServerName() request. request. { "CONTENT_LENGTH".getRealPath("/") }.

10. response). ShowCGIVariables.println("<TR><TD>" + varName + "<TD>" + varValue). if (varValue == null) varValue = "<I>(NESPECIFICATA)</I>".java Execuţia servlet-ului va avea ca rezultat imaginea din fig. i++) { String varName = variables[i][0].println("</TABLE></BODY></HTML>").10. Lista variabilelor CGI 357 . 7. out.5. } // ShowCGIVariables. HttpServletResponse response) throws ServletException. Figura 7.for(int i=0.doGet public void doPost(HttpServletRequest request. i<variables. } // for out.doPost } // ShowCGIVariables Programul 7. } // ShowCGIVariables. String varValue = variables[i][1].length. IOException { doGet(request.

11. Toate exemplele sunt rulate într-o aplicaţie cu numele pdpj.1. creată prin crearea directorului: %CATALINA_HOME%\webapps\pdpj Odată cu el s-a creat întreaga structură de sub el. POST.7. Formular pentru Maxim Textul HTML al formularului este prezentat în programul 7. cookies.1. facilitatea forward pentru servlet-uri. apelat prin GET şi prin POST Vom construi un servlet simplu care primeşte de la un formular client două numere (reprezentate prin string-uri) şi întoarce cel mai mare dintre acestea.2. 7. Figura 7.2. inclusiv fişierul web. Exemple de aplicare GET. 7. Facilităţi specifice servlet În acestă secţiune vom prezenta exemple de lucru cu metoda GET şi POST.xml vid. <html><head><title>Maximul a doua numere</title></head> <body><form action="http://localhost:8080/pdpj/servlet/Maxim" method="get" > <p>Primul numar:<input type="text" name="unu"> <p>Al doilea:<input type="text" name="doi"> 358 . Servlet-ul Maxim.11. session etc.1.2. forward 7.6. Formularul prezentat de navigator este prezentat în fig.

*. res. Rezultatul apelului Maxim Metoda de cerere este GET.getWriter().parseInt(req.12.getParameter("unu")).getParameter("doi")). IOException { int unu.setContentType("text/html").servlet. începând cu semnul de întrebare.println("</HEAD><BODY>").7. public class Maxim extends HttpServlet { public void doGet(HttpServletRequest req. apare string-ul care reprezintă cererea. De aceea se observă în URL-ul cererii către servlet că. PrintWriter out = res.parseInt(req.println("<HTML><HEAD>"). Textul sursă al servlet-ului este prezentat în programul 7. import java.servlet."+doi+ ")= "+((unu>doi)?unu:doi)+"</H1></CENTER>"). Figura 7. out. doi = Integer. răspunsul servlet-ului ajunge la client aşa cum se vede în fig. HttpServletResponse res) throws ServletException. Maxim.html După apăsarea butonului <Maxim>.http. out. import javax. out.*. out. unu = Integer. 7.6.println("<CENTER><H1>Maxim("+unu+". import javax. 359 . out. doi.<p><input type=submit value= "Maxim"> </form></body></html> Programul 7.io.*.12.println("<TITLE>Maximul a doua numere</TITLE>").println("</BODY></HTML>").

În linia a cincea a sursei servlet se va înlocui doGet cu doPost.close(). Maxim.servlet. "&"). java. 360 . penultimele patru linii ale textului sursă al servlet-ului nu sunt necesare! Dacă utilizatorul doreşte ca în acest exemplu să comunice prin metoda POST.java Pentru apelul prin metoda GET. HttpServletResponse res) throws ServletException. mai puţin string-ul de cerere care nu va apare în URL. IOException { StringTokenizer st = new StringTokenizer(req.util.*. În linia a doua a formularului HTML trebuie înlocuit method=”GET” cu method=”POST”.http. public class DisplayQueryString extends HttpServlet { public void doGet(HttpServletRequest req. res).*. 2.8. java.*.getQueryString(). servlet-ul acceptă oricare dintre metodele de cerere. } // Maxim.servlet.doPost } // Maxim Programul 7. Făcând aceste modificări.2.7. 7. javax. } // Maximum. în funcţie de cum i se transmite de la client. Servlet-ul DisplayQueryString Servlet-ul care urmează este unul simplu: el primeşte prin metoda GET un şir de cerere oarecare şi îl afişează ca răspuns pe navigatorul clientului. Cu penultimele patru linii (definirea lui doPost prin doGet – sau este posibil şi invers) prezente în sursa servlet-ului. răspunsul de pe navigator va coincide cu cel de la GET.io. IOException { doGet(req. Textul sursă al servlet-ului este dat în programul 7. import import import import javax.2.out. HttpServletResponse res) throws ServletException.doGet public void doPost(HttpServletRequest req. atunci sunt necesare doar două modificări: 1.1. String nume.*.

out. } // DisplayQueryString. Servlet-ul MaximForward Servlet-ul care urmează exemplifică una dintre facilităţile oferite de către tehnologia servlet.hasMoreTokens(). out.java În fig.13. out. } // for out.getWriter().1.println("</HEAD><BODY>").res. st. out. PrintWriter out = res.3. DisplayQueryString.8.println("<TITLE>Argumentele de apel</TITLE>"). out.13 este prezentat un exemplu de apel al acestui servlet: Figura 7. Pentru a exemplifica. ne inspirăm după servlet-ul Maxim prezentat 361 . Este vorba de posibilitatea ca dintr-un servlet să se apeleze alt servlet.doGet } // DisplayQueryString Programul 7.println("<CENTER><H1>Urmeaza "+st.nextToken().setContentType("text/html").println("<P>"+nume+"</P>"). 7. for ( . Rezultatul DisplayQueryString 7.close(). ) { nume = st.2.println("</BODY></HTML>"). out.println("<HTML><HEAD>").countTokens()+ " parametri</H1></CENTER>").

res). doi.io. IOException { ServletContext sc = getServletContext(). int unu.servlet. • Un obiect de tip RequestDispatcher. MaximForward.9.servlet. } else { unu = Integer. rd. import javax.9. Pentru realizarea de forward. import javax. Pentru aceasta. rd = sc. MaximForward va folosi două obiecte speciale: • Un obiect de tip ServletContext care deţine elementele necesare realizării acţiunii de forward. String sunu.parseInt(sunu). if ((sunu==null)||(sdoi==null)) { rd = sc. care va fi unul din două posibile.getParameter("unu").forward(req.java Prezentul servlet primeşte parametrii cererii. sdoi. va transfera spre tratare altei componente.parseInt(sdoi). HttpServletResponse res) throws ServletException. doi = Integer. În exemplul nostru.*. Apoi cere acestuia din urmă retransmiterea unei cereri şi a unui răspuns către noua resursă.html"). iar pentru răspuns. vom introduce în 362 . sunu = req. RequestDispatcher rd. care transmite cererea de forward ori spre o resursă statică a server-ului web.doGet } // MaximForward Programul 7. sdoi = req. unu = (unu>doi)?unu:doi.forward(req.http. rd.*. } // if } // MaximForward.getRequestDispatcher( "/servlet/DisplayQueryString?maxim="+unu). public class MaximForward extends HttpServlet { public void doGet(HttpServletRequest req. furnizat de obiectul ServletContext.*.getRequestDispatcher("/MaximError. Noul servlet va purta numele MaximForward şi sursa lui este prezentată în programul 7.getParameter("doi"). servlet-ul cere obiectului său context o instanţă a dispecerului de cereri (gestionat de container). import java.res).mai sus şi vom crea un nou servlet. ori spre un server.

2.servlet Deşi este cel mai natural mod de comunicare Java peste web. 7. cu tot ce am prezentat acolo. Cu acestea din urmă. prezentat în programul 7.2. literatura tratează prea puţin acest subiect [31] 7. Elemente specifice de comunicare Este natural ca între un client applet şi un servlet să existe o comunicare de o factură mai deosebită. aşa cum am arătat la CGI. un applet poate contacta un servlet folosind URLConnection. protagoniştii îşi pot transmite unul altuia obiecte Java! Evident. transmiterea de obiecte este posibilă doar folosind metoda de cerere POST.2.%CATALINA_HOME%\webapps\pdpj\ resursa statică constituită din documentul MaximError. Dacă acesta nu este corect. În mod natural. În plus.10. Comunicare applet . care vor comunica cu instanţe thread diferite ale servlet-ului. atunci determină valoarea maximă. trebuie sa dati doi parametri</h3> </body> </html> Programul 7.html de mai sus. <html><head><title>Eroare Maxim</title></head> <body> <h3>Ne para rau. 363 . deoarece este vorba de doi protagonişti Java. eventual o scriere. MaximError. atunci acestea vor fi făcute prin intermediul unor URLConnection diferite. În cazul în care string-ul de cerere este corect. conform rigorilor protocolului URLConnection (parte din HTTP). Lăsăm pe seama cititorului să testeze modul de comportare a acestui ansamblu de resurse statice şi de servlet-uri.10. atunci servlet-ul face forward către resursa MaximError. după care lansează o cerere GET cu răspunsul către servlet-ul DisplayQueryString prezentat în secţiunea precedentă.2. după care o citire şi atât! Dacă se doresc schimburi multiple. aceştia pot să-şi construiască pentru comunicare obiecte ObjectInputStream şi ObjectOutputStream.html. clientul poate să facă.html Apoi servlet-ul MaximForward verifică dacă string-ul cerere venit de la client este corect.1. În plus.

Atât documentul HTML.11. 364 .2. cât şi fişierul class al applet-ului trebuie depuse (în cazul nostru) în: %CATALINA_HOME%\webapps\pdpj\ Prin acest exemplu mai ilustrăm încă un element. Clientul Applet2Servlet Elementele acestui client sunt documentul HTML din programul 7.awt.7.*.io. <html><head><title>Aplet ce apeleaza un Servlet</title></head> <body><APPLET code="Applet2Servlet. int i.11 şi applet-ul a cărui sursă este prezentată în programul 7. ObjectInputStream in.2.class" width=500 height=300> <param name="Linia1" value="Aceasta este linia 1"> <param name="Linia2" value="Aceasta este linia 2"> <param name="Linia3" value="Aceasta este linia 3"> <param name="Linia4" value="Aceasta este linia 4"> <param name="Linia5" value="Aceasta este linia 5"> <param name="Linia6" value="Aceasta este linia 6"> <param name="Linia7" value="Aceasta este linia 7"> <param name="Linia8" value="Aceasta este linia 8"> <param name="Linia9" value="Aceasta este linia 9"> <param name="Linia10" value="Aceasta este linia 10"> <param name="Linia11" value="Aceasta este linia 11"> <param name="Linia12" value="Aceasta este linia 12"> </APPLET></body></html> Programul 7. TextArea raspuns = new TextArea(). de programare Java.*.html import import import import java.net. java. cel puţin insolit. 7.2. datorită eleganţei cu care se lucrează. java. java. ObjectOutputStream out. URLConnection urlCon.12.applet. Vom începe printr-un exemplu simplu. Applet2Servlet extends Applet { public class public void init() { URL url. Arătăm o modalitate simplă prin care un applet citeşte din documentul HTML invocator un număr variabil de parametri.2 (transformarea unui şir prin CGI). Applet2Servlet.*. reluare a exemplului prezentat în 4.În mai multe dintre exemplele care urmează am preferat schimbul de obiecte între applet şi servlet.*.

this. Prin intermediul acestora. ((l = getParameter("Linia" + i))!= null).setVisible(true). Acesta este apoi depus pentru a fi afişat pe navigator. pereche cu cele duale din applet. out. applet-ul citeşte de la servlet. // Fa applet-ul vizibil raspuns.setDoOutput(true). // Depune liniile de raspuns } // try catch(Exception e) { e.getHost()+ ":8080/pdpj/servlet/Servlet2Applet"). Se creează un obiect out de tip ObjectOutputStream prin intermediul căruia se trimite obiectul String la servlet. // Permite si scrierea in resursa out = new ObjectOutputStream(urlCon. url = new URL("http://"+getCodeBase(). } // catch } // Applet2Server.close(). se configurează aşa încât pe ea să se poată citi şi scrie. out.getOutputStream()).writeObject(s). Din toate aceste linii la applet se formează un singur string. // Adauga obiectul la applet this.printStackTrace(). va primi un obiect String şi va întoarce obiectul String cu toate 365 . s = (String)in.3. El suprascrie metoda doPost. l. clientul applet îşi extrage din tag-urile <PARAM existente în a.html liniile pe care le va transmite servlet-ului spre a le transforma în litere mari. try { for (i=1. in. urlCon. Apoi se deschide conexiunea URL. folosind obiectul in de tip ObjectInputStream. i++) s += l+"\n".setText(s).2.close(). iar din obiectul răspuns un obiect ObjectOutputStream. Applet2Servlet.setUseCaches(false). iar transmisia să se facă direct.add(raspuns).2.setDefaultUseCaches(false). urlCon. // Deschide conexiunea urlCon.readObject(). În ea îşi creează din obiectul cerere un obiect ObjectInputStream.12. Servlet-ul Servlet2Applet Perechea servlet pentru clientul prezentat mai sus este extrem de simplă. Pentru partea a doua – receptarea răspunsului. urlCon = url.java Mai întâi.openConnection(). un obiect de tip String. in = new ObjectInputStream(urlCon.String s="". 7. fără staţionări prin zone catch.getInputStream()).init } // Applet2Servlet Programul 7.

literele transformate în litere mari. HttpServletResponse res) throws ServletException.io.servlet. } // catch } // Servlet2Applet.writeObject(s). import java.14. s = s.getInputStream()). În mod firesc. String s = (String)in.close(). out. 4. import javax.class în: %CATALINA_HOME%\webapps\pdpj\WEB-INF\classes şi restartarea Tomcat. efectul execuţiei este cel din fig. in. el se aseamănă foarte mult cu cel din fig.*.doPost } // Servlet2Applet Programul 7.readObject(). 366 . ObjectOutputStream out = new ObjectOutputStream(res.*. out. Servlet2Applet. out.*.toUpperCase().12.13. public class Servlet2Applet extends HttpServlet { public void doPost(HttpServletRequest req.13.java După depunerea fişierului Servlet2Applet. Sursa servlet-ului este prezentată în programul 7. 7.servlet.printStackTrace().http. import javax.getOutputStream()).close(). IOException { try { ObjectInputStream in = new ObjectInputStream(req. } // try catch (ClassNotFoundException e) { e.flush().

Efectul apelului applet <--> servlet 367 .Figura 7.14.

titularul lor este server-ul generator.7. • controlul unor elemente de protecţie şi securitate a comunicaţiei între browser şi server-ul web. permit vizualizarea acestora. Prelucrarea cookies în servlet trebuie să respecte anumite cerinţe. Prin acest mecanism.3. Cookies nu se pot vizualiza în IE.2. • ajustarea parametrilor unui site în funcţie de clientul care îl consultă.3. Analog. se poate consulta: Edit / Preferences / Privacy & Security / Cookies / manager / Cookies manager. înainte de citirea parametrilor. Printre sarcinile pe care informaţiile cookie le pot îndeplini enumerăm: • identificarea unui user în timpul unei sesiuni e-commerce. Un element de mare nesiguranţă privitor la cookies este faptul că orice utilizator al unui navigator poate să-i comande acestuia ştergerea cookies! Pentru informaţii privind cookies la Internet Explorer. ca de exemplu Mozilla. deşi comunicarea web este fără stare.1. • optimizarea căutării frecvente a unor informaţii – reţinerea unor parametri mai des invocaţi. Deoarece cookies sunt transmise prin header-ul cererii. acesta trebuie să trimită mai întâi cookies şi abia apoi răspunsul efectiv. Noţiunea de cookie Cookies sunt mici informaţii textuale pe care un server web le generează şi le trimite la un browser spre a fi accesate ulterior. utilizatorul poate consulta: Tools / Internet options / Delete cookies. acestea trebuie citite primele. spaţiul total limitat la 4Ko etc. Din păcate. browser-ul le returnează neschimbate server-ului generator. 368 . Pentru Mozilla. Ca şi cifre aproximative. Utilizare cookies 7. valoare). reprezentate ASCII şi fără spaţii în interiorul lor. server-ul Web se poate folosi de aceste informaţii. • controlul username – password în doi paşi.2. Altele. Trebuie reţinut faptul că deşi navigatorul găzduieşte cookies. Din punct de vedere formal. unele browsere nici măcar nu permit vizualizarea acestor cookies. Mai mult. De fapt. mecanismul este folosit adesea pentru a depăşi restricţia comunicării fără stare (stateless client/server communication) între browser şi server-ul web. La un nou apel al browserului spre server-ul web. atunci când servlet-ul transmite cookies spre navigator. un cookie este o pereche de forma (nume. spaţiul rezervat pentru cookies este limitat. unele browsere acceptă maximum 20 cookies/site şi 300 total.

} // if } // for // // Aici este locul pentru prelucrarea parametrilor de intrare // res.util. 369 .*. În schimb. de câte ori sunt invocate fiecare dintre nume care apar în cererile de la un anumit client (browser).*.2. Concret. k. i++ ) { k = v.Nu intenţionăm să prezentăm API cookies.nextElement())).2. vom prezenta în secţiunea următoare un exemplu de utilizare cookies care acoperă multe dintre aspectele de utilizare a acestora.io. prin cookies. v. // e contine toate numele // parametrilor Vector v = new Vector(). Sursa acestui servlet este prezentată în programul 7. java. // Urmeaza depunerea Cookies vechi for ( i=0. javax.3.setValue(""+(1+ Integer.hasMoreElements().indexOf(tc[i]. // numele parametrilor sunt puse in vector for ( i=0. java. i<tc.remove(k). Cookie c.servlet. import import import import javax.http. if (k >=0 ) { // un nume de parametru coincide cu un nume de Cookie tc[i]. i++ ) { tc[i]. e. public class CookieCountParameters extends HttpServlet { public void doGet(HttpServletRequest req.getWriter(). 7.setContentType("text/html"). valoare).parseInt(tc[i].*.getName()). // Inca nu exista cookies Enumeration e = req. PrintWriter out = res.add((String)e. HttpServletResponse res) throws ServletException. Cookie[] tc = req. servlet-ul îşi propune să contorizeze. v. IOException { int i.14.length.getParameterNames().getValue()))).getCookies(). // tc contine toate Cookies if (tc == null) tc = new Cookie[0]. i<tc.setMaxAge(60*60*24*365). ştiut fiind că orice cerere de la un client este o pereche de string-uri (nume.length.*. for ( .servlet. Exemplu de utilizare cookies Exemplul pe care îl prezentăm este de fapt o schemă cadru pentru servlet-uri care doresc să utilizeze cookies.

length.println("<h3>Cookies noi</h3>"). fiecare dintre cookies va avea drept nume exact numele care este transmis prin cerere. "1"). Se compară apoi numele cookies din tabloul tc cu numele de parametri cerere actuali din v.println("<p>"+tc[i]. out. atunci tc va fi un tablou cu 0 elemente. 7.println("</HEAD><BODY>").res. CookieCountParameters. res. out. În cazul în care de la navigator nu vine nici un cookie. se dă ca răspuns un document HTML cu numerele tuturor cookies şi numerele lor de apariţie. out.println("<TITLE>Lista cookies</TITLE>").println("<HTML><HEAD>"). for ( i=0. 370 .java Pentru realizarea scopului propus. valoarea cookie corespunzătoare din tabloul tc este mărită cu o unitate şi numele respectiv este şters din vectorul v. i<v. După aceea.setMaxAge(60*60*24*365). aşa cum se vede în fig. Servlet-ul CookieCountParameters reţine în tabloul tc toate cookies primite de la navigator.println("<p>"+((String)v. i++ ) out.close().getValue()+"</p>").addCookie(c). în obiectul enumerare e se reţin numele câmpurilor din cerere. i++ ) { c = new Cookie((String)v. out.size().15. } // CookieCountParameters. după care se depun în vectorul v (pentru o prelucrare mai uşoară).addCookie(tc[i]). out. În final.size(). c.elementAt(i).14.println("</BODY></HTML>").println("<h3>Cookies vechi</h3>"). } // for // Urmeaza depunerea Cookies noi. În caz de coincidenţă.getName()+ " = "+tc[i]. numai pentru a se evidenţia pentru client acţiunea servlet-ului. Valoarea fiecărui cookie este un întreg reprezentând de câte ori browser-ul a invocat în cereri numele respectiv. out. i++ ) out. ramase in v for (i=0. Pasul următor constă în fixarea vârstei maxime (un an) atât pentru cookie vechi. cât şi pentru cele noi şi adăugarea fiecăreia dintre ele în obiectul răspuns res. i<tc. i<v.doGet } // CookieCountParameters Programul 7. } // for // // Aici este locul raspunsului HTML dat de servlet // out. for ( i=0.elementAt(i))+"</p>").

Figura 7. Numărarea parametrilor prin cookies 371 .15.

3 (contor de pagini Web).4. • se pot depune obiecte cărora li se asociază nume (metoda setAttribute). • simularea unei sesiuni prin intermediul construcţiilor „ascunse” la client. telegrafic.4.7...2. 372 .”>. Un obiect HTTPSession poate desfăşura.2. într-un fel sau altul dependente de client. • adăugarea la URL de către client a unor câmpuri cu rol de informaţii de tip session. Exemplul care urmează ilustrează utilizarea obiectelor HTTPSession la counteri de pagini Web. sub forma: <input type=”hydden” name=”session” value=”. printre altele. Pentru detalii se poate consulta API HTTPSession [89. Exemplul este unul similar (şi inspirat de) cap. modalităţile prin care se obişnuieşte asigurarea comunicării Web „statefull”. Sesiuni servlet 7. Credem că aici este locul de a prezenta. 4. Informaţiile sunt păstrate la server sub forma unor perechi (nume . să se asigure comunicare cu stare (statefull client/server communication) între clienţi şi server-ul Web. Conceptul de sesiune Obiectele HTTPSession au fost introduse pentru a permite. Formal. aceste informaţii ajung la server care le poate interpreta ca şi obiecte de tip session. toate celelalte fiind. • întreţinerea unui mecanism cookies cu fiecare navigator. acţiunile: • răspunde dacă este un obiect nou sau nu (metoda isNew). 91]. • obiecte HTTPSession. Este evident că ultima soluţie este cea veritabilă. un obiect de tip HTTPSession poate reţine informaţii între două invocări succesive ale servlet-ului din cadrul aceleiaşi lansări a browser-ului. prin intermediul servlet-urilor. obiect). • etc. • se pot extrage obiecte din el invocându-se numele asociat (metoda getAttribute).1.7.

Invocarea acestuia în cadrul paginii se face prin tag-ul: 373 . Fişierul binar poate fi creat. Prezenta implementare utilizează: • Un fişier binar numit FileServletCounter. CounterInit. f. } //CounterInit. public class CounterInit { public static void main(String a[]) throws Exception { RandomAccessFile f.io.1.2.7. if (a.exit(1). • Un servlet ServletCounter sau ServletCounterSession. Servlet-ul trimite spre client un obiect Integer conţinând numărul de accese. care este directorul curent al containerului de servlet. În 7. } // if f = new RandomAccessFile(a[0].close(). clientul efectiv al counter-ului este un applet. Implementarea unui counter de pagină Web Aşa cum am prezentat şi în 4.*. în modul cel mai simplu. "rw").15. f. de exemplu.length != 2) { System.java 7.main } // CounterInit Programul 7.4. prin intermediul unui fişier binar care reţine întregul reprezentând numărul curent de accese.2.writeInt(i). prin intermediul programului 7.parseInt(a[1]).println("Apel: java CounterInit "+ "numeFisier valoareInitialaContor").2. după caz (diferenţa dintre ele o prezentăm mai târziu).out.4 am expus şi alte metode de plasare a fişierelor locale.4.4. System. Pentru a fi accesat de servlet. am ales plasarea lui în $CATALINA_HOME/bin.3 un counter Web este realizat. import java.3. • Un client applet AppletCounter care să preia obiectul Integer şi să-l afişieze pe pagină într-un obiect grafic Label.7. int i. Clientul counter Aşa cum am spus mai sus.15. i = Integer.

awt. urlCon. AppletCounter. try { url = new URL("http://"+getCodeBase(). urlCon.*. URLConnection urlCon.16.close(). in. } // try catch(Exception e) { e. 374 . java.4. urlCon = url.init } // AppletCounter Programul 7. import import import import java. this.intValue()). AppletCounter extends Applet { public class public void init() { URL url. raspuns = new Label(""+i.setUseCaches(false).printStackTrace().readObject(). i = (Integer)in.applet.net.16. Integer i.add(raspuns).*.class" width="50" height="30"> </APPLET> Textul sursă al applet-ului este dat în programul 7.2.io.17.setDefaultUseCaches(false). this.*. in = new ObjectInputStream(urlCon.setVisible(true).openConnection().<APPLET code="AppletCounter.getInputStream()).*. java. java. În cazul apelării lui ServletCounterSession. } // catch } // AppletCounter.getHost()+ ":8080/pdpj/servlet/ServletCounter"). ServletCounter Sursa acestui acestui servlet este dată în programul 7. se citeşte obiectul Integer de la servlet şi se afişează grafic sub forma unui Label. singura modificare este înlocuirea numelui servlet-ului.java În cadrul metodei init se stabileşte conexiunea URL cu servlet-ul. 7. ObjectInputStream in.4. Label raspuns.

ServletCounter.writeObject(new Integer(i)). IOException { int i=0. // Citeste numarul i++. 375 . HttpServletResponse res) throws ServletException. // Trimite numarul la client out. // Rescrie noul numar f.seek(0). S-au definit atât metoda doPost cât şi doGet.doPost public void doGet(HttpServletRequest req. pentru a se evita multiacces simultan la fişierul contor.*. import java. După incrementare se creează obiectul out de tip ObjectOutputStream prin care se trimite applet-ului obiectul Integer care conţine numărul de accese.flush().servlet.io. // Incrementeaza f.setContentType("application/octet-stream"). RandomAccessFile f. out. "rw").doGet } // ServletCounter Programul 7. res).readInt(). în absenţa trimiterii unui mesaj de la client nu se poate stabili implicit metoda de cerere.http.getClass()) { // Blocheaza pentru incrementare f = new RandomAccessFile("FileServletCounter". } // ServletCounter. synchronized (this.getOutputStream()). // Repozitioneaza la inceput f. } // ServletCounter. out. HttpServletResponse res) throws ServletException.java Trebuie mai întâi să remarcăm faptul că accesul la fişierul binar este sincronizat cu clasa servlet-ului.servlet destinat contorizării accesului la o pagină Web. out = new ObjectOutputStream(res. // Deschide fisierul i = f.close().close(). // Inchide fisierul } // synchronized res.*. Lăsăm pe seama cititorului să testeze acest tandem applet .17. IOException { doPost(req. ObjectOutputStream out.servlet.*.import javax. import javax. public class ServletCounter extends HttpServlet { public void doPost(HttpServletRequest req. deoarece.writeInt(i).

import java. out = new ObjectOutputStream(res. Pentru aceasta vom folosi un obiect HTTPSession. // Rescrie noul numar f. } // synchronized } else { // sesiune exista deja i = ((Integer)sesiune. import javax. } // if res.io. Efectul va fi că numărul de accese va creşte. trebuie să se oprească navigatorul după care să se lanseze din nou un acces la aceeaşi pagină!.4.writeInt(i). import javax.5. // Repozitioneaza la inceput f.servlet.getAttribute( "contorCurent")). // Inchide fisierul sesiune. // Incrementeaza f.http.writeObject(new Integer(i)). ServletCounterSession De ce a fost nevoie de un alt servlet? Dintr-un motiv foarte simplu. ObjectOutputStream out.seek(0). // Citeste numarul i++.7. HttpServletResponse res) throws ServletException. RandomAccessFile f. out.close().getOutputStream()).*.readInt(). sesiune = req. new Integer(i)).*. Accesaţi o pagină ce este contorizată cu ServletCounter definit mai sus. "rw"). Sursa servlet-ului ServletCounterSession este dată în programul 7. IOException { int i=0.intValue(). HttpSession sesiune.getSession(true).isNew()) { synchronized (this. Din această cauză vom prezenta un servlet modificat care va fi „imun” cu numărarea în situaţiile expuse mai sus. // Creeaza o sesiune daca nu exista if (sesiune. public class ServletCounterSession extends HttpServlet { public void doPost(HttpServletRequest req. Ba chiar mai simplu decât <BACK>: cereţi navigatorului <RELOAD> şi numărul de accese va creşte din nou! Evident că această numărare (cel puţin după unii) nu este una corectă. // Trimite numarul la client 376 .getClass()) { // Blocheaza pentru incrementare f = new RandomAccessFile("FileServletCounter".setContentType("application/octet-stream"). Apoi daţi <BACK> la browser şi accesaţi din nou pagina.2.servlet. El va considera un acces doar primul acces al browser-ului la pagină.18.*.setAttribute("contorCurent". // Deschide fisierul i = f. Pentru a se număra două accese.

doGet } // ServletCounterSession Programul 7.3. } // ServletCounterSession.1. trebuie efectuată una dintre cele trei acţiuni prin care această arhivă devine vizibilă în zona classpath.close().18.1\lib\j2ee. Se testează apoi dacă obiectul sesiune este unul nou. } // ServletCounterSession. locul ei este în: c:\j2sdkee1. Trimiterea de mesaje e-mail din servlet Pentru compilarea şi execuţia programelor din această aplicaţie este nevoie ca în zona classpath să poată fi accesată atât arhiva servlet. se extrage din el obiectul cu numele asociat contorCurent şi se trimite acesta clientului.jar Aşa cum am arătat în 7. out. j2ee. cât şi o nouă arhivă. 7. Dacă obiectul sesiune nu este unul nou. atunci se introduce în el un obiect Integer care conţine valoarea curentă a contorului.out. 377 . IOException { doPost(req. res). 7. Acestui obiect i se asociază numele contorCurent. Dacă da. HttpServletResponse res) throws ServletException. Câteva exemple mai complexe Aceste exemple.3.3.java De fapt este uşor de văzut că este acelaşi servlet ca şi precedentul. Mai întâi din parametrul cerere req s-a creat obiectul sesiune de tip HTTPSession.4. sunt incluse în aplicaţia pdpj.3. la fel ca şi cele din secţiunea precedentă. ServletCounterSession.jar. Ultima arhivă se află în distribuţia "Java Enterprise Edition". Noi am preferat să adăugăm permanent această arhivă la variabila CLASSPATH. cu foarte mici completări. În această distribuţie.doPost public void doGet(HttpServletRequest req.flush().3. j2ee1.jar.1.1.

16.16.html Exemplul care urmează reprezintă versiunea română a celui similar descris în [2]. Intrarea se face printr-un formular prezentat în fig. Clientul mail.1.ubbcluj. 7.7.1.cs. Formular de trimitere a unui mesaj Documentul HTML de descriere a acestui formular este dat în programul 7.3.ro:8080/"+ "pdpj/servlet/ServletMail" METHOD="POST"> <CENTER> <H1>Compuneti un mesaj</H1><HR><BR> <TABLE ALIGN="center" WIDTH="100%" CELLSPACING="2" CELLPADDING="2"> <TR> <TD ALIGN="right">Numele:</TD> 378 . <HTML><HEAD><TITLE>Compunere de mesaj</title></HEAD> <BODY> <FORM ACTION="http://florin. Figura 7.19.

1.2. 379 . cunoscute şi sub numele de pachetul JavaMail. mail.mail.mail şi javax. expeditor de mesaje email Trimiterea mesajelor e-mail din Java se poate face în mai multe feluri.internet. Clasa Mailer. aceea oferită de către pachetete javax.html 7.19.3. Ne vom opri doar la una dintre modalităţi.4</OPTION> <OPTION VALUE="SQL Server">SQL</OPTION> </SELECT> </TD> <TD ALIGN="right">Operating System:</TD> <TD> <SELECT NAME="ddlb_os" size="1"> <OPTION VALUE="W2k">Windows 2000</OPTION> <OPTION VALUE="Linux">Linux</OPTION> <OPTION VALUE="Solaris">Solaris</OPTION> </SELECT> </TD> </TR> </TABLE> </CENTER> <BR>Continutul mesajului: <BR><TEXTAREA NAME="txtProblem" COLS="50" ROWS="4"> </TEXTAREA><HR><BR> <CENTER> <INPUT TYPE="Submit" NAME="btnSubmit" VALUE="Trimite mesajul"> </CENTER> </FORM></BODY></HTML> Programul 7.<TD><INPUT TYPE="Text" NAME="txtFirst" ALIGN="LEFT" SIZE="15"></TD> <TD ALIGN="right">Prenumele:</TD> <TD><INPUT TYPE="Text" NAME="txtLast" ALIGN="LEFT" SIZE="15"></TD> </TR> <TR> <TD ALIGN="right">Email:</TD> <TD><INPUT TYPE="Text" NAME="txtEmail" ALIGN="LEFT" SIZE="25"></TD> <TD ALIGN="right">Telefon:</TD> <TD><INPUT TYPE="Text" NAME="txtPhone" ALIGN="LEFT" SIZE="15"></TD> </TR> <TR> <TD ALIGN="right">Software:</TD> <TD> <SELECT NAME="ddlb_software" SIZE="1"> <OPTION VALUE="Word">Microsoft Word</OPTION> <OPTION VALUE="Java">Java SDK1.

mail şi javax.Aceste pachete nu se află în distribuţia standard j2sdk1. • cc (copy carbon) indică eventuale alte adrese la care se va trimite mesajul (poate fi vidă). Acest obiect va fi explorat de API JavaMail.Properties.sun. 5. Din obiectul Session se creează un obiect Message. 380 .com doar pachetul JavaMail. 3. Bcc. • to indică adresa destinatarului. eventual şi câmpurile Cc. Se creează un obiect Session JavaMail.4 (distribuţia Enterprise Edition). În absenţa distribuţiei j2skdee. trebuie parcurşi următorii şapte paşi: 1. se poate descărca de la java. Se va crea un obiect java.4 (distribuţia Java Standard).send().jar Arhiva j2ee. cu informaţii despre server-ul de mail. Metoda esenţială este una statică: Mailer. .util.4\lib\j2ee. pentru trimiterea unui mesaj e-mail prin protocolul SMTP folosind JavaMail. To.internet Clasa Mailer. sau dacă se doreşte adresele destinatarilor separate prin virgulă. 2. 6. În cazul nostru această arhivă este luată din distribuţia j2sdkee1. Se trimite mesajul folosind metoda statică Transport. este destinată trimiterii de mesaje e-mail. Pentru compilarea şi pentru execuţia unui astfel de program trebuie să aibă specificat în classpath calea spre o arhivă jar care conţine JavaMail. pe care o prezentăm în continuare. Subject. Se încarcă obiectul Properties cel puţin cu adresa maşinii din reţeaua locală care este server SMTP.jar conţine pachetele javax. 7. În esenţă.send( . dar se găseşte în distribuţia j2sdkee1.mail. ) Această metodă are ca şi parametri şapte string-uri (nu există legătura cu cei 7 paşi de mai sus): • from indică adresa expeditorului. Se depune textul efectiv al mesajului în corpul acestuia.4: c:\j2sdkee1. Se setează în obiectul Message câmpurile From. 4. .

Textul sursă Mailer.*.mail.} setMailHost(String mailHost) {this.} getText() {return text. /** Mailer este o clasa destinata trimiterii unui email */ public class Mailer { protected Session session. javax. // Adresele destinatarilor (Cc:) protected String bcc. return true. } // Mailer. if ((from.text = text. // Adresele destinatarilor din (Bcc:) protected String subject.*.isComplete 381 .} setText(String text) {this.} public boolean isComplete() { if ((from==null)||(to==null)||(subject==null)|| (text==null)||(mailHost==null)) return false. • mailHost conţine adresa maşinii locale care este server SMTP. dar un destinatar nu vede care sunt ceilalţi destinatari (poate fi vidă).mailHost = mailHost.length()==0)|| (subject.length()==0)) return false.} setSubject(String subject) {this. javax. import import import import java.from = from. java.} setBcc(String bcc) {this. • text conţine efectiv mesajul de transmis.} getCc() {return cc.20.} getBcc() {return bcc.} setFrom(String from) {this.mail.internet.} setTo(String to) {this.*.length()==0)|| (text.cc = cc.} getSubject() {return subject.• bcc (blint copy carbon) cu acelaşi rol ca şi cc. // Obiectul sesiune javamail protected String from. • subject conţine linia cu subiectul mesajului.} getMailHost() {return mailHost. // Linia Subject: protected String text. // Adresele destinatarilor (To:) protected String cc.util.to = to.*. // Adresa serverului local SMTP public public public public public public public public public public public public public public String String String String String String String void void void void void void void getFrom() {return from.} setCc(String cc) {this.length()==0)||(mailHost. // Corpul mesajului protected String mailHost.length()==0)||(to.} getTo() {return to.subject = subject.io.java este prezentat în programul 7.bcc = bcc. // Adresa expeditorului protected String to.

i<adr. i++) adr[i] = new InternetAddress(st. // Adresele din Cc: mesg. Properties props = new Properties().length.host".getDefaultInstance(props.countTokens()].TO. if (lista==null) return adr.setRecipients(Message.start().adrese public synchronized void doSend() throws MessagingException. adrese(to)). } // Mailer. // Creeaza un mesaj mesg.setRecipients(Message. // Creeaza obiectul sesiune final Message mesg = new MimeMessage(session). // Adresele din Bcc: mesg. // Fixeaza corpul mesajului // Trimite mesajul prin metoda statica Transport.nextToken().length()==0) return adr.setFrom(adrese(from)[0]). // Se vor transmite la MAIL API props. // Fixeaza From: in mesaj mesg.send(mesg).doSend 382 . null).BCC.protected InternetAddress[] adrese(String lista) throws AddressException { InternetAddress[] adr = new InternetAddress[0].smtp. // (Din ratiuni de eficienta. // Adresele din To: mesg.RecipientType. AddressException { if (!isComplete()) throw new IllegalArgumentException( "nu se poate apela inainte de completarea mesajului"). StringTokenizer st = new StringTokenizer(lista. adrese(cc)). adrese(bcc)). if (session==null) session = Session.setSubject(subject). // thread de trimitere } // Mailer.RecipientType.CC.setText(text). ".put("mail. } // catch } // run }. mailHost). // Fixeaza Subject in mesaj mesg.RecipientType. } // try catch (MessagingException e) { throw new IllegalArgumentException( "Eroare la transport:" +e).setRecipients(Message. trimiterea se face intr-un thread separat) new Thread() { public void run() { try { Transport.trim()). if (lista. adr = new InternetAddress[st."). return adr. for (int i=0.

) îşi creează propriul obiect Mailer. "". String cc. folosind în acest scop metoda isComplete(). String subject.setBcc(bcc).ubbcluj.fboian@yahoo. de asemenea. Din raţiuni de eficienţă.doSend(). String to. utilizatorul poate să-şi declare un obiect de tip Mailer şi să apeleze metoda doSend() a acestui obiect.setTo(to).setFrom(from). m. m. Dacă aceste câmpuri se completează individual. 383 . String mailHost) throws MessagingException { Mailer m = new Mailer(). String bcc. . în maniera specifică componentelor Java (JavaBeans). De fapt. Mailer. Sursa Mailer se termină cu o metodă main. m.send( . În interiorul metodei doSend se poate vedea definirea celor şapte paşi necesari trimiterii unui e-mail. "Asta-i textul\ncu doua linii". "Test Mailer multiplu".ubbcluj.main } // Mailer Programul 7.setSubject(subject). apoi identifica protected "from". .cs.ubbcluj. că metoda doSend începe prin a controla completarea rubricilor obligatorii ale unui e-mail.send public static void main(String a[]) throws Exception { Mailer.// Metoda apelabila din exterior pentru trimitere e-mail public static void send(String from. m.ro". "florin@scs. doSend(). după care apelează. trimiterera se efectuează într-un thread separat. m.setMailHost(mailHost). String text. "florin@cs.ro". prin metode getter şi setter.setCc(cc). m. } // Mailer.send("florin@cs.setText(text).java Cei şapte parametri pot fi setaţi şi individual.send(). metoda adrese transformă adresele date prin string-uri întrun tablou de obiecte de tip InternetAddress. folosită doar pentru a putea testa mai uşor clasa. m. /* La executie programul se va conecta la "mailHost" (daca administratorul permite. completează câmpurile cu valorile parametrilor. În sfârşit. la rândul ei. metoda Mailer. m. De remarcat. dupa care trimite mesajul */ } // Mailer.ro").20. Cititorul poate renunţa la acesta şi să trimită direct prin Transport. "nessie.ubbcluj.com".ro.

String _text. break. buffer.java.buffer.buffer.3. auxiliar pentru servlet Clasa HTML este concepută pentru ca servlet-ul să fie ajutat să realizeze mai uşor o serie de construcţii HTML simple sau tabele.add public boolean addTable(String _labels[].append("<hr>"). int HEADING = 1.buffer.buffer. this.buffer.append("<body>").buffer.append("<br>"). cel mai simplu este ca fişierul class al ei să fie depus în acelaşi director cu servlet-ul: %CATALINA_HOME%\webapps\pdpj\WEB-INF\classes Sursa este prezentată în programul 7.buffer.buffer. break. } // HTML.7. Pentru ca servlet-ul să o poată folosi. this.append(_title).append(_text). this.append(_text). ResultSet _rs){ int cols = _labels.21. case HEADING: this. int LINE = 2.3. break.sql. 384 .append("</title></head>"). case LINE: this. boolean _break) { switch(_style) { case NORMAL: this.append("<h1>"). import java.append("<html><head><title>").1. Clasa HTML. Lăsăm pe seama cititorului să deducă din sursă rolul acestei clase. } // if } // HTML. this. } // switch if(_break) { this.length. public HTML(String _title) { buffer = new StringBuffer(4096).buffer. this. this. default: break. this. public class HTML { public static final public static final public static final public StringBuffer int NORMAL = 0.buffer.append("<table width='100%'>").buffer.HTML public void add(int _style.append("</h1>").*.

append("<tr>").*. } // HTML.append(_rs.buffer.this. PrintWriter out = res. this.*.http. HTML.append("<tr>").setContentType("text/html").buffer.printStackTrace().next()) { this. IOException { res.append("</table>").servlet.getString(i)). 385 . for (int i = 1. getParameters(req). } // for this.servlet. } // while } // try catch (SQLException e) { e.buffer. } // for this.1.buffer.buffer. msgFrom.buffer. public void doPost(HttpServletRequest req. javax. return true. } // HTML. i <= cols.append("<th align='left'>").io. for (int i = 0.3.4.*. i < cols.buffer. i++) { this.22. public class ServletMail extends HttpServlet { String message.buffer. return false. java.getPage } // HTML Programul 7.buffer.addTable public String getPage() { return this.append(_labels[i]).append("</td>"). i++) { this. java.toString() + "</body></html>". this. msgSubject. ServletMail Sursa acestui servlet este dată în programul 7. try { while (_rs.java 7. this.buffer.append("</tr>").append("</tr>").append("<td>"). this.*.21.buffer. import import import import javax. HttpServletResponse res) throws ServletException.getWriter().buffer.append("</th>"). } // catch this. msgTo.util.

"Mesajul a fost transmis". out. ServletMail.getPage()). tempStringBuffer. tempStringBuffer. tempStringBuffer.append("\n"). } // try catch (Exception e) { e. tempStringBuffer. "".getParameter("txtLast")).append("\n").toString(). message = tempStringBuffer.getParameters } // ServletMail Programul 7.getParameter("txtPhone")). folosind clientul de mail pine [98].getParameter("txtEmail")).append("De la: ").append("\n"). msgSubject = "Trimitere de mesaj".ro.ro".append("\n\n"). "".doPost private void getParameters(HttpServletRequest req) throws ServletException. message.getParameter("txtFirst")).append(req. tempStringBuffer. } // catch HTML h = new HTML("Transmitator de mesaje mail").ubbcluj. out. msgFrom = "florin@cs. 386 .append("\n\n").printStackTrace().append("Software: ").append(req. tempStringBuffer.send(msgFrom. tempStringBuffer. tempStringBuffer. msgTo = req.getParameter("ddlb_os")). 7.append(" ").getParameter("ddlb_software")). return.add(HTML.append("\n").append(req.append("Telefon: ").append(req. "nessie. tempStringBuffer.HEADING.close().getParameter("txtProblem")). false).16. } // ServletMail. tempStringBuffer. tempStringBuffer. tempStringBuffer.getParameter("txtEmail").append(req.scs. msgSubject. iar poşta este citită pe maşina linux. tempStringBuffer.append(req.ubbcluj. 7. care prezintă mesajul completat ca în fig. tempStringBuffer. IOException { StringBuffer tempStringBuffer = new StringBuffer(1024).cs. tempStringBuffer.append("Email: ").17.ubbcluj. tempStringBuffer. tempStringBuffer.22. msgTo.println(h. } // ServletMail. tempStringBuffer. h.ro").java Efectul execuţiei acestui servlet este prezentat în fig.append(req.append("OS: ").append("Mesajul: "). tempStringBuffer. tempStringBuffer.try { Mailer.

Înlocuim comunicaţia RMI utilizarea servlet-urilor. Înlocuim păstrarea datelor (CATALOG) ca obiecte serializate cu păstrarea acestora în două tabele dintr-o bază de date. Un server Student. 2. în %CATALINA_HOME%\webapps\ 387 .adăugări.de către un server Profesor a unor perechi (Materie.Profesor) şi a unor triplete (Student. care apelează la rândul lui server-ul Profesor. Este vorba de gestionarea .Materie. Aplicaţia Note.Nota). În această secţiune reluăm problema.2. versiunea servlet În 5. ştergeri. permite accesul prin Web al fiecărui student să-şi vadă doar propriile note. cât şi pentru maşina Student. dar schimbăm două elemente esenţiale în implementare: 1.17.3. Mesajul citit cu pine 7.5 am prezentat şi implementat sub RMI aplicaţia Note. modificări .Figura 7. Această aplicaţie va purta numele Note şi atât pentru maşina Profesor.

valoare += rs.getMaterieNota 388 .1. db = new NivelJDBC().next().executeQuery("SELECT Profesor FROM Perechi "+ "WHERE Materie = '"+materie+"' and Profesor = '"+profesor+"'"). import java.AccessDB public boolean isMaterieProfesor(String materie. String profesor) throws Exception { rs = db.JdbcOdbcDriver".getString("Materie")+"\n".sql.odbc. } // for return nume+"\t"+valoare.getString("Nota")+"\n".2. conectabilă la ACCESS.executeQuery("SELECT Materie.*. valoare="".isMaterieProfesor public String getMaterieNota(String student) throws Exception { String nume.jdbc.*. for ( nume="". ResultSet rs.next(). } // NoteAccessDB. public class NoteAccessDB { NivelJDBC db = null.3.16) pentru încărcarea iniţială a bazei de date prin linii date la intrarea standard. rs = db. } // NoteAccessDB.3) prin care am exemplificat utilizarea clasei NivelJDBC (programul 3. împreună cu toată substructura necesară. } // NoteAccessDB. avem deja un astfel de program: este vorba de TestNivelJDBC (programul 3. ) { nume += rs.util. este dată în programul 7. Sursa clasei.setConnection("sun. Pentru prezenta aplicaţie. import java. aşa cum am arătat în 7. return rs. Clasa NoteAccessDB Această clasă va fi folosită de către ServletProfesor pentru a accesa tabelele Perechi şi Triplete din baza de date. i = db.3. public NoteAccessDB() throws Exception { int i.trebuie creat un director Note.1. valoare. "jdbc:odbc:Note").2).5. Nota FROM Triplete"+ " WHERE Student = '"+student+"'"). rs. 7.23. În varianta RMI am folosit BatchCatalogDB (programul 5.

for ( nume="". Clasa oferă trei metode: − − − isMaterieProfesor.nextToken(). valoare="". StringTokenizer stt = new StringTokenizer(studentnota. rs. rs = db. "\t").2. nota. valoare.executeQuery("SELECT Student.nextToken().setStudentNota public static void main(String a[]) throws Exception { new NoteAccessDB(). "+ "Nota FROM Triplete "+ "WHERE Student = '"+student+ "' and Materie = '"+materie+"'"). } // for } // NoteAccessDB.nextToken(). } // NoteAccessDB. clasa foloseşte un obiect de tip NivelJDBC. stt.next().getString("Student")+"\n".java Pentru acces la baza de date. setStudentNota. getStudentNota. valoare += rs. else db. Nota FROM Triplete"+ " WHERE Materie = '"+materie+"'").nextToken(). ) { nume += rs. rs = db.23. for ( .main } // NoteAccessDB Programul 7.next()) db. ) { student = stt. stt. stt.getString("Nota")+"\n". } // for return nume+"\t"+valoare.nextToken(). student. if (rs. } // NoteAccessDB.getStudentNota public void setStudentNota(String studentnota) throws Exception { int i.hasMoreTokens().executeQuery("SELECT Student.'"+nota+"')").executeUpdate("UPDATE Triplete SET Nota='"+nota+"'"+ "WHERE Student = '"+student+ "' and Materie = '"+materie+"'").executeUpdate("INSERT INTO Triplete VALUES ('"+ student+"'. nota = stt. prezentat în programul 3.public String getStudentNota(String materie) throws Exception { String nume. materie = stt. 389 . Materie. NoteAccessDB.'"+materie+"'. String materie.

String profesor) Verifică dacă în tabelul Perechi există perechea (materie. nota. Primele două cuvinte sunt ignorate (dar trebuie transmise de apelator!). adică profesor predă sau nu materie. Dacă perechea nu există. separate tot prin <NEWLINE> ('\n'). urmează un separator <TAB> ('\t') după care notele la fiecare materie. atunci se introduce un nou triplet (student.Evident.nota).3. drept răspuns. În funcţie de numărul de cuvinte conţinute metoda doPost trimite clientului. Al treilea cuvânt este materie pentru care se actualizează notele. În continuare apar un număr oarecare de perechi de cuvinte student. Unicul parametru de intrare este un string cu un conţinut trimis de servlet. atunci doPost interpretează acest cuvânt ca şi numele unui student. separate prin caracterul <NEWLINE> ('\n'). Printr-un obiect de tip NoteAccessDB accesează baza de date şi apoi îi întoarce clientului (student) notele lui la toate materiile. 390 . Se verifică mai întâi dacă perechea (student.2.materie) există deja şi în caz afirmativ se actualizează doar nota. profesor).2.materie. Programul ServletProfesorDB Acest servlet primeşte de la client un obiect String. corespunzător cerinţelor. toate trei accesează. Metoda boolean isMaterieProfesor(String materie. după caz. după caz adaugă. Metoda setStudentNota(String studentnota) Actualizează sau. 7. notele studenţilor la o anumită materie. tabelele bazei de date. Metoda String getMaterieNota(String student) Întoarce pentru student notele lui la toate materiile. El conţine o succesiune de cuvinte separate prin caracterul <TAB> ('\t'). Metoda întoarce true sau false. Dacă string-ul de intrare conţine un singur cuvânt. În el se află mai întâi numele materiilor. Acest string conţine unul sau mai multe cuvinte separate prin caracterul <TAB> ('\t'). un alt obiect String specific. String-ul rezultat are forma potrivită pentru a putea fi preluat simplu de către metodele apelatoare.

Dacă string-ul de intrare are mai mult de trei cuvinte. out = new ObjectOutputStream(res.servlet. parola invalida sau nu preda materia 391 . if ((!AuthFTP. profesor))) { out = new ObjectOutputStream(res.24 import import import import javax.isFTPUser("localhost".writeObject("").getOutputStream()).getMaterieNota(student). out. parola şi materie.writeObject(student). parola. Sursa acestui servlet este prezentată în programul 7. parola = st. javax. java. ObjectOutputStream out.util. În caz de eşec întoarce clientului string-ul vid.servlet. } // Intoarce esec. out. profesor. materie = st. profesor.nextToken().io.http. if (ntokens == 1) { student = (String)adb. HttpServletResponse res) throws ServletException. out. student = (String)in.getInputStream()).close().*. Dacă string-ul de intrare are exact trei cuvinte.*. out. in = new ObjectInputStream(req. materie="". String student.nextToken().countTokens().Dacă are trei sau mai multe cuvinte. pentru a actualiza notele la materia respectivă. st = new StringTokenizer(student. return. return. atunci transmite acest şir metodei setMaterieNota a obiectului NotaAccessDB.close(). "\t"). in. atunci îi întoarce clientului (profesor) notele tuturor studenţilor la materia respectivă. java. int ntokens = st.close(). parola))|| (!adb. atunci primele trei sunt interpretate ca şi profesor. Mai întâi se autentifică profesorul pe baza parolei şi apoi se verifică dacă predă materia respectivă.*. NoteAccessDB adb = new NoteAccessDB().nextToken(). public class ServletProfesorDB extends HttpServlet { public void doPost(HttpServletRequest req.getOutputStream()). } // Intoarce StudentNota if (ntokens >= 3) { profesor = st. IOException { try { ObjectInputStream in. StringTokenizer st.isMaterieProfesor(materie.*.readObject().

IOException { doPost(req. împreună cu celelalte fişiere necesare. // Fixeaza notele studentilor la o materie out = new ObjectOutputStream(res. out.class. NoteAccessDB.writeObject(student). compus din două cuvinte separate printr-un caracter <TAB> ('\t'). } // try catch (Exception e) { e. trebuie ca pe maşina Profesor.java Pentru ca acest servlet să funcţioneze.24.printStackTrace().2.class. ServletProfesorDB.class. NivelJDBC. în: %CATALINA_HOME%\webapps\Note\WEB-INF\classes\ să fie copiat fişierul class al servlet-ului.} // A trecut de autentificare if (ntokens == 3) { student = adb.getOutputStream()). Deci în classes trebuie să existe fişierele: • • • • ServletProfesorDB.doPost public void doGet(HttpServletRequest req.getOutputStream()). HttpServletResponse res) throws ServletException. out.close().3. } // catch } // ServletProfesorDB. } // ServletProfesorDB.3. } // Intoarce studentii si notele pentru o materie adb. Primul cuvânt este numele studentului iar al doilea este parola lui.setStudentNota(student). AuthFTP. Servlet-ul autentifică 392 .getStudentNota(materie).class. Programul ServletStudentDB Acest servlet este apelat prin Web de către studenţi pentru a-şi afla notele. Clientul (student) transmite servlet-ului un obiect te tip String. out = new ObjectOutputStream(res. out. res).writeObject("OK"). 7. out. return.close().doGet } // ServletProfesorDB Programul 7.

aşa cum am descris la metoda getMaterieNota a clasei NoteAccessDB. După această conectare. ServerProfesor îi întoarce un obiect String. Acest servlet trebuie să fie iniţializat folosind fişierul web.xml (aplicaţia Note) Cu aceste precizări.studentul. prin conexiunea obţinută de la obiectul răspuns (res). Deci.25.io. import import import import import javax.com/dtd/web-app_2_3. Inc.servlet.3//EN" "http://java. Dacă autentificarea se face cu succes.*. javax. web.http. java. pe maşina Student trebuie să existe fişierul: %CATALINA_HOME%\webapps\Note\WEB-INF\web.25.0" encoding="ISO-8859-1"?> <!DOCTYPE web-app PUBLIC "-//Sun Microsystems.sun. ServletStudent îi transmite lui ServletProfesor un obiect String ce conţine numele studentului.*. <?xml version="1.*.net. atunci ServletStudent apelează. public class ServletStudentDB extends HttpServlet { private String masina = null. ServletProfesor prin URLConnection.*.xml a se preciza adresa maşinii ServletProfesor.util. 393 . iar în caz de eşec îi întoarce acestuia un obiect String de lungime zero. invităm cititorul să consulte sursa acestui servlet în programul 7.//DTD Web Application 2.dtd"> <web-app> <servlet> <servlet-name>ServletStudentDB</servlet-name> <servlet-class>ServletStudentDB</servlet-class> <init-param> <param-name>MasinaProfesor</param-name> <param-value>localhost</param-value> </init-param> </servlet> </web-app> Programul 7.xml Conţinutul acestui fişier este prezentat în programul 7.*. Prin aceeaşi conexiune. java. la rândul lui.26. trimite clientului obiectul String primit de la ServletProfesor.servlet. Apoi. java.

substring(student.close(). IOException { try { URL url. URLConnection urlCon.doPost public void doGet(HttpServletRequest req. HttpServletResponse res) throws ServletException. in = new ObjectInputStream(urlCon. out.openConnection(). // Deschide conexiunea urlCon.indexOf("\t")+1).getInputStream()). out. student = (String)in. parola.writeObject(student). student = student.close().init public void doPost(HttpServletRequest req. } // try catch (Exception e) { e. IOException { doPost(req. } // catch } // ServletStudentDB. parola)) { out = new ObjectOutputStream(res.getInitParameter("MasinaProfesor"). out = new ObjectOutputStream(res. out. if (masina == null) masina = "localhost".readObject(). 394 . urlCon. in. } // ServletStudentDB.writeObject("").setDoInput(true). out.public void init(ServletConfig config) throws ServletException { super. urlCon = url. if (!AuthFTP.getOutputStream()). urlCon.getOutputStream()).printStackTrace().getInputStream()). out. student.getOutputStream()). student = (String)in.close().setDefaultUseCaches(false). // Permite si scrierea in resursa urlCon.writeObject(student). masina = config. res). parola = student. ObjectOutputStream out.close().setUseCaches(false).setDoOutput(true).isFTPUser("localhost". out = new ObjectOutputStream(urlCon. HttpServletResponse res) throws ServletException. student. } // Intoarce esec url = new URL("http://"+masina+ ":8080/Note/servlet/ServletProfesorDB").init(config). in.readObject().indexOf("\t")). return. String student. in = new ObjectInputStream(req. out. ObjectInputStream in.close().substring(0.

add(l).class" width=300 height=400> Applet-ul se conectează la ServerStudentDB prin URLConnection.*. GridBagLayout gb = new GridBagLayout(). După ce studentul îşi completează numele şi parola.*. tfParola.*. java.gridy = 0. this. Button citeste. Apelul applet-ului dintr-un document HTML se face prin: <applet code="ClientStudentDB. l = new Label("Materie").3.doGet } // ServletStudentDB Programul 7.awt.CENTER.anchor = GridBagConstraints.awt. java. GridBagConstraints gc = new GridBagConstraints(). gc. import import import import import import java. ObjectOutputStream out. taValoare. gc. TextField tfUser.io.util.event. Din punctul de vedere al interfeţei cu utilizatorul este identic cu clientul prezentat în 5. URLConnection urlCon. apasă butonul <CITESTE>. textul sursă al applet-ului este prezentat în programul 7.*. l = new Label("Nota"). java. gc). 395 . java.6.setConstraints(l.4. this.26. gc.27.2. ObjectInputStream in. În acest moment este autentificat şi în caz de succes i se afişează notele. URL url. ServletStudentDB.} // ServletStudentDB. public void init() { try { Label l. public class ClientStudentDB extends Applet implements ActionListener { TextArea taNume.setLayout(gb). java.gridx = 0.*.*.net.5.2. Clientul StudentDB Acesta este un client Web care apelează servlet-ul prin intermediul unui applet.applet.java 7. gb.

anchor = GridBagConstraints. gb.anchor = GridBagConstraints.gridx = 0. gc. gc. gc).add(l).WEST. gc.getHost()+ 396 . gc. gc.gridy = 2.anchor = GridBagConstraints. gb. this. gc). this.CENTER. gc). gc). gc.gridx = 0. gc. gc.add(taValoare).add(tfUser).add(tfParola). tfParola = new TextField(10).10). gb.add(taNume). gc). gc.add(citeste).gridx = 1. gc).setConstraints(tfUser. taValoare = new TextArea(10. 10). this.gridy = 3. tfUser = new TextField(10). gc.gridx = 1. citeste.setEditable(false).gridx = 0.setConstraints(l.setVisible(true). gc. taValoare.setConstraints(taValoare.setConstraints(l. tfParola. gc.anchor = GridBagConstraints. gc. gb. gc.add(l).WEST. gc. this. taNume = new TextArea(10. gc). gc. gc).EAST. this.anchor = GridBagConstraints.gridx = 1. gc. this.add(l). this. gc.anchor = GridBagConstraints. this.setEditable(false). gb. gc.gridy = 0. gc.EAST.setEchoChar('*').WEST.gridy = 1.setConstraints(taNume. gc. gb.gridx = 0.setConstraints(l.gridx = 1.gridy = 4.getPreferredSize()).gridy = 3.WEST. gb. l = new Label("Student:"). this.gridy = 2.setConstraints(tfParola. this. gc. gb. gc.gridy = 1.EAST.setConstraints(citeste.setSize(this.addActionListener(this). url = new URL("http://"+getCodeBase().anchor = GridBagConstraints. taNume. citeste = new Button("Citeste").gc. l = new Label("Parola:").anchor = GridBagConstraints.

close(). el poate fi scris ca un program simplu.openConnection().27. Poate deci să se elimine accesul la 397 . } // catch } // ClientStudentDB. // Permite si scrierea in resursa } // try catch (Exception e) { e. // Deschide conexiunea urlCon. out.materie!"). valoare. try { if ((ev.setText(nume).init public void actionPerformed(ActionEvent ev) { String nume.nume. urlCon = url.":8080/Note/servlet/ServletStudentDB").6.printStackTrace().getActionCommand()).printStackTrace().setDefaultUseCaches(false).setUseCaches(false). urlCon.substring(nume. if (nume.getText()). nume = (String)in. taValoare. out. } else tfUser.2.5. Din punct de vedere al implementării este o aplicaţie Java Standalone care interfaţează cu utilizatorul printr-un Frame.close(). in = new ObjectInputStream(urlCon.3.java 7.getOutputStream()). nume = nume.5.1.getText()+"\t"+ tfParola.setDoOutput(true). ClientStudentDB. cu două părţi: o interfaţă grafică şi o interfaţă cu baza de date. tfUser. taNume.parola. Dat fiind faptul că ClientProfesorDB este un program standalone. in. urlCon.equals("Citeste")) { out = new ObjectOutputStream( urlCon.writeObject(tfUser.indexOf("\t")). } // catch } // ClientStudentDB.substring(0.setText(valoare).readObject(). } // if tratare citeste } // try catch (Exception e) { e.length() > 0) { valoare = nume.actionPerformed } // ClientStudentDB Programul 7.setText("OK citire").setText("ERR!citire:user.getInputStream()). ClientProfesorDB Comportarea în relaţia cu utilizatorul a acestui client este identică cu cea a clientului descris în 5.indexOf("\t")+1).

URL url. gc). tfParola. acest client discută cu mai multe instanţe thread ale servlet-ului. Button citeste.28 sursa acestui client. public ClientProfesorDB(String host) throws Exception { Label l. Conexiunea dintre cei doi parteneri se face prin URLConnection. tfMaterie.setLayout(gb). java. de fapt. care permite numai o eventuală scriere spre server urmată. programul transmite la ServerProfesorDB un obiect String şi primeşte înapoi ca şi răspuns un alt obiect de tip String.gridy = 0.*. gc. eventual.awt.util.net.gridx = 1. import import import import import java.add(l). URLConnection urlCon. l = new Label("Nota"). java. gc.io. gc. java. TextField tfUser. class ClientProfesorDB extends Frame implements ActionListener.*. gb. 398 . ObjectOutputStream out.setConstraints(l. scrie.gridy = 0. GridBagLayout gb = new GridBagLayout().*. la un client care cere servlet-ului să acceseze baza de date. WindowListener { TextArea taNume. Cu aceste precizări.gridx = 0. din motive care se vor vedea imediat. la fiecare operaţie de citire sau de scriere se deschide o nouă conexiune! Deci. ObjectInputStream in.*. prezentăm în programul 7. java. GridBagConstraints gc = new GridBagConstraints(). Pentru a se permite oricâte operaţii şi în orice ordine. taValoare.ServletProfesorDB pentru a accesa baza de date. this. gc. ATENŢIE! Acest client poate cere ori de câte ori să citească şi să scrie. de o citire de la el.CENTER. Noi vom rămâne.awt. this.*. Acest fapt încalcă cerinţele protocolului HTTP.anchor = GridBagConstraints. l = new Label("Student"). gc.event. Din punct de vedere al conexiunii. Lăsăm pe seama cititorului să conceapă un astfel de program.

gc). gc). gc.EAST. gb.anchor = GridBagConstraints. gb. this. gc.anchor = GridBagConstraints. this. gc. gc. gc).setConstraints(tfUser. l = new Label("Profesor:").anchor = GridBagConstraints.WEST.setConstraints(tfMaterie.setEchoChar('*'). taValoare = new TextArea(10.gridy = 3. gc.anchor = GridBagConstraints. gc).add(tfParola).gridy = 1.add(l). tfParola = new TextField(10).anchor = GridBagConstraints. gb. gc.gridy = 4. gc.anchor = GridBagConstraints.EAST. tfParola.addActionListener(this). this. 399 .WEST.gridy = 2.gridx = 0.gridy = 3. gb. this.EAST. gc.gridy = 1.setConstraints(taValoare. gb.setConstraints(l.gridy = 2.gridx = 1. gb. gc). gc. l = new Label("Parola:"). gc. gc.setConstraints(l.add(l). gc. gc). gc. l = new Label("Materie:"). gc.WEST.gridy = 4. gb.anchor = GridBagConstraints. this.gc. 10).gridx = 1. gc.add(l).gridx = 0. gc. gc). gc.anchor = GridBagConstraints. gc. this. gb.gridx = 1.gridx = 0. tfUser = new TextField(10). this.add(taNume). tfMaterie = new TextField(10).CENTER. taNume = new TextArea(10.anchor = GridBagConstraints. gc. gc. this. gc.setConstraints(l. gc).setConstraints(tfParola. gc).10). citeste = new Button("Citeste").add(taValoare).EAST. this.add(tfMaterie).add(l). gc. citeste.add(tfUser).gridx = 0. gc.WEST. gc.gridx = 1.setConstraints(l.setConstraints(taNume. gb.

anchor = GridBagConstraints.gridx = 1.substring(nume.setConstraints(citeste. String nume = "". int i. taNume.setUseCaches(false). this. std. taValoare. // Permite si scrierea in resursa urlCon.equals("Scrie")) { stp = new StringTokenizer(taNume. out.indexOf("\t")).addWindowListener(this).close().parola. scrie = new Button("Scrie").close().setText("OK citire"). out = new ObjectOutputStream(urlCon. this.getOutputStream()).getText()). gc.gc.gridy = 5. gc).setDoOutput(true). this.pack().show(). gb.substring(0. } // if tratare citeste if ((ev. in. gc. 400 . std = new StringTokenizer(taValoare.equals("Citeste")) { url = new URL("http://localhost:8080/Note/"+ "servlet/ServletProfesorDB").getText()+"\t"+tfMaterie. this. gc.setDefaultUseCaches(false).setText("ERR!citire:user.add(citeste). gc).add(scrie). urlCon. this. scrie.EAST. urlCon = url.getText().WEST. } else tfUser. gb. nume = nume.getText()+"\t"+ tfParola.setText(valoare).setVisible(true). if (nume. this. this.getInputStream()).getActionCommand()). gc.openConnection().setDoInput(true). in = new ObjectInputStream(urlCon.nume.ClientProfesorDB public void actionPerformed(ActionEvent ev) { StringTokenizer stp.gridx = 0. out. gc.setText(nume).setConstraints(scrie.getActionCommand()).writeObject(tfUser. } // ClientProfesorDB. "\r\n\t").gridy = 5.indexOf("\t")+1). valoare = "".anchor = GridBagConstraints. n.materie!"). urlCon.addActionListener(this).length() > 0) { valoare = nume. setTitle("Fixare note/materie"). tfUser. // Deschide conexiunea urlCon. try { if ((ev.getPreferredSize()).readObject().setSize(this.getText(). nume = (String)in.

n = stp.countTokens(). url = new URL("http://localhost:8080/Note/servlet"+ "/ServletProfesorDB").main } // ClientProfesorDB Programul 7.close().nextToken(). if (nume. out.} windowClosed(WindowEvent e) {} windowActivated(WindowEvent e) {} windowDeactivated(WindowEvent e) {} windowOpened(WindowEvent e) {} windowIconified(WindowEvent e) {} windowDeiconified(WindowEvent e) {} public static void main(String a[]) throws Exception { new ClientProfesorDB((a.getText()+"\t"+tfParola. } // catch } // ClientProfesorDB. out = new ObjectOutputStream(urlCon. // Deschide conexiunea urlCon.nextToken()+"\t"+std. urlCon = url.setUseCaches(false).28.materie!").countTokens(). i<n.actionPerformed public public public public public public public void void void void void void void windowClosing(WindowEvent e) {System.printStackTrace(). i++) nume += "\t"+stp. n = (n > i) ? i : n.getInputStream()). i = std.length() >0) tfUser.setText("ERR!scriere:user.length >0)?a[0]:"localhost").readObject().writeObject(nume).close().setDoOutput(true).getOutputStream()).getText()+"\t"+ tfMaterie.java 401 .setText("OK scriere"). else tfUser. in = new ObjectInputStream(urlCon. out.parola.openConnection(). urlCon. } // if tratare scrie } // try catch (Exception e) { e. } // ClientProfesorDB.getText().setDoInput(true). urlCon. nume = tfUser.setDefaultUseCaches(false). // Permite si scrierea in resursa urlCon. in.exit(0). ClientProfesorDB. nume = (String)in."\r\n\t"). for (i=0.

practic codul devine complet independent de specificul unui anumit tip de bază de date.xml un nume JNDI... parola acestuia sunt fixate direct în cod. numele user-ului bazei de date. Să le luăm pe rând.3.7.4.. Alinierea Tomcat la JNDI simplifică această activitate şi face aplicaţiile mult mai scalabile.. string-ul de conectare. sarcina detalierii parametrilor bazei urmând să fie legaţi doar de această referinţă.5.3. prin nişte constante. • să asigure în codul Java conectarea la baza de date prin referinţa de mai sus. aşa cum am văzut în 7.xml resursa baza de date şi să o lege de numele ei de referinţă. Pentru realizearea acestui mod de legare. O soluţie de a face mai elastică această legare este aceea de a transmite aceste date ca şi parametri de iniţializare.</res-auth> <res-sharing-scope> .1.1. Conectare JDBC din Tomcat prin JNDI Unul dintre dezavantajele conectării directe la o bază de date prin JDBC este acela că informaţiile de conectare: • • • • clasa driver. Descrierea unei astfel de referinţe se face prin intermediul unei substructuri XML înglobată în tag-ul <resource-ref>.</res-sharing-scope> </resource-ref> 402 . Definirea referinţei la resursa bază de date Prin această referinţă servlet-urile aplicaţiei se vor referi la baza de date.3. Se elimină astfel legarea directă a servlet-ului de un anumit tip de bază de date. • să definească în server... prin care programele să se refere la resursa bază de date.. 7.</res-type> <res-auth> . proiectantul aplicaţiei trebuie să efectueze trei mici acţiuni: • să definească în web. Structura acestuia este: <resource-ref> <res-ref-name> .3. Astfel.</res-ref-name> <res-type> ..

Să exemplificăm. Acesta este un nume JNDI relativ la contextul java:comp/env şi trebuie să fie unic în cadrul aplicaţiei.3.xml al ei va fi cel din programul 7. res-sharing-scope indică gradul de partajabilitate al resursei.3//EN" "http://java.2. după cum am mai arătat în 3. aşa că de multe ori referinţa este întâlnită sub forma: java:comp/env/jdbc/numeConexiune res-type indică tipul clasei Java utilizat. Dacă dorim ca pentru aplicaţia Note descrisă mai sus accesul să se facă prin JNDI. Valorile posibile sunt Container sau Application. res-ref-name este numele referinţei la resursă. Inc.xml numai după tag-urile care se referă la parametri ai servlet-urilor. tag-urile resource-ref trebuie să fie specificate în web. clasa DataSource. Valoarea poate fi Shareable .sun. De obicei.29. În cazul JDBC prin JNDI.DataSource</res-type> 403 . această clasă este. <?xml version="1.sql.implicit -sau Unshareable.//DTD Web Application 2.Conform specificaţiilor Tomcat. atunci fişierul web. pentru baze de date acest nume are forma: jdbc/numeConexiune În codul Java.dtd"> <web-app> <servlet> <servlet-name>ServletStudentDB</servlet-name> <servlet-class>ServletStudentDB</servlet-class> <init-param> <param-name>MasinaProfesor</param-name> <param-value>localhost</param-value> </init-param> </servlet> <resource-ref> <res-ref-name>jdbc/NoteDB</res-ref-name> <res-type>javax.com/dtd/web-app_2_3. acest nume este de obicei ataşat contextului lui.0" encoding="ISO-8859-1"?> <!DOCTYPE web-app PUBLIC "-//Sun Microsystems. res-auth specifică cine autorizează accesul la resursă.

xml. nici fişierul web.DataSource ds = (DataSource)cx.<res-auth>Container</res-auth> </resource-ref> </web-app> Programul 7. Utilizarea referinţei în codul Java Aşa cum am arătat în 3. nici codul Java al aplicaţiei nu se vor modifica! Ceea ce se va modifica prezentăm în secţiunea următoare.29.setConnection("java:comp/env/jdbc/NoteDB").2. Se va apela astfel cea de-a doua metodă de conectare din clasa NivelJDBC (programul 3.2). această referinţă este utilizată în locul conexiunii directe la baza de date.odbc.sql.lookup("java:comp/env/jdbc/numeConexiune"). în locul inrtrucţiunii: i = db. javax. 7. web. secvenţa minimală este: javax. Rugăm cititorul să compare acest fişier cu cel din programul 7.sql. În cazul mutării aplicaţiei.getConnection().DataSource ds = (DataSource)cx. Cititorul poate refolosi extrem de simplu codurile claselor aplicaţiei Note pentru a alinia aplicaţia la JNDI. Evident.2. se va folosi instrucţiunea: i = db.sql.naming. 404 .Connection co = ds.23).25.3.naming.Context cx = new InitialContext().3. "jdbc:odbc:Note"). În acest program.Context cx = new InitialContext(). În cazul aplicaţiei Note.3. javax. sau a schimbării motorului de bază de date.xml (Note JNDI) Referinţa am numit-o jdbc/NoteDB. Pentru fabricarea în acest mod a unei conexiuni JNDI la o bază de date.sql. Singura modificare în cod este în clasa NoteAccessDB (programul 7. javax. javax.lookup("numeJNDIBazaDate").JdbcOdbcDriver".setConnection("sun.jdbc.xml trebuie configurat aşa cum am arătat în secţiunea precedentă. conexiunea se obţine prrin: javax.getConnection(). fişierul web.Connection co = ds.

în programul 7. Definirea resursei baza de date Definirea acestei resurse se face în fişierul server. Pentru aplicaţia Note am folosit ca şi în 7.odbc.2. MySQL.3. despre care am mai vorbit în 7. deoarece Access nu pretinde neapărat o autentificare pe bază de nume de user.3. Resursa a fost definită prin cei patru parametri caracteristici resursei: • user Specifică numele utilizatorului care accesează motorul de bază de date.3.7. resursa bază de date) Partea referitoare la resursa bază de date este cuprinsă în tag-ul <ResourceParams>. <Context path="/Note" docBase="Note" debug="0" privileged="true"> <ResourceParams name="jdbc/NoteDB"> <parameter> <name>user</name> <value/> </parameter> <parameter> <name>password</name> <value/> </parameter> <parameter> <name>driverClassName</name> <value>sun. o bază de date Access aflată pe maşina locală a containerului.2. Pentru fixarea ideilor. 405 .1.xml (parţial. aşa cum o face ORACLE. MSSQLServer. server.3.jdbc.3.1. am indicat ca şi valoare a acestui nume şirul vid. În cazul nostru.30 prezentăm tag-ul <CONTEXT al aplicaţiei Note. aplicaţia Note.30. fie în contextul aplicaţiei în care se foloseşte resursa.wml de configurare a Tomcat. Această definire se poate face fie în contextul implicit Tomcat (<DefaultContext>).JdbcOdbcDriver</value> </parameter> <parameter> <name>driverName</name> <value>jdbc:odbc:Note</value> </parameter> </ResourceParams> </Context> Programul 7. care conţine în el şi definirea resursei baza de date.

• password Este parola de acces la bază. atunci acesta trebuie să fie prezent în: $CATALINA_HOME/common/lib/ spre a putea fi accesat de către containerul Tomcat.2. Atenţie! Pentru situaţiile în care se impune o parolă.xml trebuie bine securizat. 406 .2. Atenţie însă: Dacă pentru driver este necesară o arhivă jar (este vorba de driver ce nu este în distribuţia Java). fişierul server. Absenţa adresei maşină face ca implicit ea să coincidă cu adresa locală (unde se află Tomcat). • driverName. deoarece apare parola în clar! • driverClassName Indică numele clasei driver pentru acces la baza de date.2. de asemenea aici are ca valoare şirul vid. Lăsăm pe seama cititorului să testeze aplicaţia Note prin JNDI şi pe celelalte tipuri de baze de date prezentate în 3. sau url (sunt echivalente) Indică string-ul de conectare la baza de date.