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

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

rpm pentru platformele Linux. setarea unei variabile VAR se face: o Pentru Windows 9x.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.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.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.4.0 Setarea variabilelor de mediu se face în conformitate cu cerinţele specifice ale sistemului de operare pe care se lucrează. atunci containerul poate fi instalat cu următoarea comanda Linux. o Pentru Unix se depun. instalarea poate să difere. după caz.apache. Dacă s-a optat pentru formatul . . fie un director al utilizatorului.gz pentru ambele platforme.profile fie în ~/.exe pentru platformele Windows.o C:\jakarta-tomcat-4.1 • JAVA_HOME prin care vom marca locul în structura de directori unde se află instalat pachetul Java. De exemplu.zip sau tar. De exemplu.org. pe o staţie Windows 2000 această variabilă poate fi: o C:\j2sdk1. După caz. Daca s-a optat pentru formatul . fie în ~/. Dacă s-a optat pentru formatul . • Instalarea containerului Tomcat În funcţie de formatul în care a fost descărcat containerul Tomcat. executată ca root: o rpm –Uhv $CATALINA_HOME 330 .exe se execută fişierul exe descărcat. fie în /etc/profiles.0. .gz se dezarhivează acest fişier în $CATALINA_HOME.rpm.

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

• 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

. iar într-un tag <HOST apar mai multe tag-uri interioare lui. alteAtr .</CONTEXT> . Distribuţia Tomcat are definit în fişierul de configurare numai un singur host virtual.. În interiorul tag-ului <HOST se definesc tag-urile <CONTEXT..definire caracteristici context prin taguri interioare . > .. /> sau <CONTEXT path="/nume" docBase="caleReala" . > Aşa cum am arătat mai sus... > .<CONTEXT path="/nume" docBase="caleReala" . 84]...Tag-ul <HOST defineşte pentru container un host virtual... Atributul appBase defineşte prin dirAplicatii subdirectorul lui $CATALINA_HOME destinat să găzduiască aplicaţiile web. 38.. Maniera de specificare în cele două tag-uri este specifică limbajului XML [87.. 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. 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). ne vom opri numai la două atribute principale: name şi appBase.. Din tag-ul <CONTEXT ne interesează numai atributele path şi docBase....<HOST name="adresaMasina" appBase="dirAplicatii" . Pe lângă aceste atribute mai sunt şi altele. definit prin: <HOST name="localhost" appBase="webapps" .- Un tag <HOST defineşte un host virtual. partea legată de definirea contextelor Tomcat este: ... 336 . Schematic.. alteAtr . numele contextului fiind acelaşi cu numele directorului.. Atributul name al lui specifică prin adresaMasina adresa reală a maşinii care găzduieşte host-ul virtual. alteAtribute .</HOST> .

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

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

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

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

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

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

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

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

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

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

5.6.7. 7. Figura 7. Prin aceasta.7. Imaginea la client va fi cea din fig.Figura 7.6. Figura 7. 7. Al doilea apel al servlet-ului Primul Să înlocuim în sursa servlet-ului Primul (programul 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. Apel servlet Primul cu text/plain 347 .1) "text/html" cu "text/plain". navigatorul va interpreta răspunsul ca un text simplu şi va afişa imaginea din fig. unde se poate vedea şi URL-ul de apel.

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

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

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

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

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

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

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

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

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"). { "DOCUMENT_ROOT".getPathInfo() }. { "SCRIPT_NAME". out. IOException { response. { "CONTENT_TYPE".1).getMethod() }.io.getRemoteAddr() }. { "REMOTE_ADDR".getWriter(). request. { "QUERY_STRING". request. String. request. { "PATH_INFO". javax. getServerInfo() Programul 7.getQueryString() }.*.getServerPort()) }. request.0 sau HTTP/1.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. { "CONTENT_LENGTH". HttpServletResponse response) throws ServletException. request. import import import import java.getServerInfo() } }.getAuthType() }.*.getPathTranslated() }. { "SERVER_PROTOCOL".*. { "SERVER_SOFTWARE". { "REMOTE_USER". SERVER_SOFTWARE Informaţii despre server-ul Web getServletContext().getServerPort() Name şi versiunea protocolului SERVER_PROTOCOL de cerere request.getServletPath() }. getServletContext().getRealPath("/") }.getProtocol() ( HTTP/1. java.getProtocol() }. { "PATH_TRANSLATED". getServletContext().*.http. { "REQUEST_METHOD". String. { "SERVER_NAME". request.servlet. PrintWriter out = response.valueOf(request.getRemoteHost() }.getRemoteUser() }.getServerName() request. request. request. public class ShowCGIVariables extends HttpServlet { public void doGet(HttpServletRequest request. request. 356 .5 afişează conţinutul variabilelor CGI dintr-un servlet. String[][] variables = { { "AUTH_TYPE". request. request.util.setContentType("text/html"). request. { "SERVER_PORT". javax.getServerName() }.servlet.valueOf( request.getContentType() }. { "REMOTE_HOST".getContentLength()) }.

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

POST. Facilităţi specifice servlet În acestă secţiune vom prezenta exemple de lucru cu metoda GET şi POST. facilitatea forward pentru servlet-uri.11. Exemple de aplicare GET. Servlet-ul Maxim.2. 7. 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. Formular pentru Maxim Textul HTML al formularului este prezentat în programul 7. <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 . Toate exemplele sunt rulate într-o aplicaţie cu numele pdpj. cookies.xml vid.2. Figura 7. forward 7.1.1.2.6. 7. session etc.1.7. creată prin crearea directorului: %CATALINA_HOME%\webapps\pdpj Odată cu el s-a creat întreaga structură de sub el.11. Formularul prezentat de navigator este prezentat în fig. inclusiv fişierul web.

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

servlet.close().*.doPost } // Maxim Programul 7. HttpServletResponse res) throws ServletException. "&"). Maxim.util.java Pentru apelul prin metoda GET.2. În linia a doua a formularului HTML trebuie înlocuit method=”GET” cu method=”POST”. javax. 7. 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. Textul sursă al servlet-ului este dat în programul 7. String nume. java. 2.doGet public void doPost(HttpServletRequest req. } // Maxim.2.*. IOException { StringTokenizer st = new StringTokenizer(req. import import import import javax. 360 . În linia a cincea a sursei servlet se va înlocui doGet cu doPost.out.7. java.http. Făcând aceste modificări. HttpServletResponse res) throws ServletException. IOException { doGet(req. res). public class DisplayQueryString extends HttpServlet { public void doGet(HttpServletRequest req. în funcţie de cum i se transmite de la client. servlet-ul acceptă oricare dintre metodele de cerere. mai puţin string-ul de cerere care nu va apare în URL. 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.*. Cu penultimele patru linii (definirea lui doPost prin doGet – sau este posibil şi invers) prezente în sursa servlet-ului.io.servlet.8. } // Maximum.getQueryString().1. răspunsul de pe navigator va coincide cu cel de la GET. atunci sunt necesare doar două modificări: 1.*.

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

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

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

364 .12. reluare a exemplului prezentat în 4. java.*.2.2. int i. Vom începe printr-un exemplu simplu.html import import import import java. <html><head><title>Aplet ce apeleaza un Servlet</title></head> <body><APPLET code="Applet2Servlet. ObjectOutputStream out. Atât documentul HTML.io.*.*. 7.awt. URLConnection urlCon.applet.2 (transformarea unui şir prin CGI). 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. cel puţin insolit. ObjectInputStream in.7.În mai multe dintre exemplele care urmează am preferat schimbul de obiecte între applet şi servlet. TextArea raspuns = new TextArea(). java. java. Clientul Applet2Servlet Elementele acestui client sunt documentul HTML din programul 7.*.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.11. datorită eleganţei cu care se lucrează.net.2. Arătăm o modalitate simplă prin care un applet citeşte din documentul HTML invocator un număr variabil de parametri.11 şi applet-ul a cărui sursă este prezentată în programul 7. de programare Java. Applet2Servlet. Applet2Servlet extends Applet { public class public void init() { URL url.

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

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

14. Efectul apelului applet <--> servlet 367 .Figura 7.

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

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

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

Figura 7.15. Numărarea parametrilor prin cookies 371 .

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

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

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

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

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

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

ubbcluj. Intrarea se face printr-un formular prezentat în fig. 7. <HTML><HEAD><TITLE>Compunere de mesaj</title></HEAD> <BODY> <FORM ACTION="http://florin.1.1. Formular de trimitere a unui mesaj Documentul HTML de descriere a acestui formular este dat în programul 7. Figura 7.19.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 Exemplul care urmează reprezintă versiunea română a celui similar descris în [2].16.16.7.cs. Clientul mail.3.

Ne vom opri doar la una dintre modalităţi.1.2.internet.19.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. Clasa Mailer. aceea oferită de către pachetete javax.3.html 7. mail.mail şi javax. 379 . expeditor de mesaje email Trimiterea mesajelor e-mail din Java se poate face în mai multe feluri. cunoscute şi sub numele de pachetul JavaMail.mail.

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

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

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

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

} // if } // HTML. this.append("<hr>"). this. buffer.java.append("<body>").21.3. case LINE: this. Lăsăm pe seama cititorului să deducă din sursă rolul acestei clase. import java. int HEADING = 1.buffer. 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.append(_text).append(_text). 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.buffer. this.buffer.append("<br>").7.append(_title). } // HTML.3. 384 .sql.append("</h1>"). ResultSet _rs){ int cols = _labels. this.append("<h1>").HTML public void add(int _style. this. } // switch if(_break) { this. default: break.*.buffer.length. case HEADING: this.buffer. Pentru ca servlet-ul să o poată folosi.buffer. int LINE = 2. String _text. break.add public boolean addTable(String _labels[]. public class HTML { public static final public static final public static final public StringBuffer int NORMAL = 0. this. Clasa HTML.buffer.buffer. break. boolean _break) { switch(_style) { case NORMAL: this.append("</title></head>"). public HTML(String _title) { buffer = new StringBuffer(4096).buffer. break.buffer.1.append("<table width='100%'>"). this.append("<html><head><title>").

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

atunci acesta trebuie să fie prezent în: $CATALINA_HOME/common/lib/ spre a putea fi accesat de către containerul Tomcat. 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. Absenţa adresei maşină face ca implicit ea să coincidă cu adresa locală (unde se află Tomcat). • driverName. Atenţie însă: Dacă pentru driver este necesară o arhivă jar (este vorba de driver ce nu este în distribuţia Java). deoarece apare parola în clar! • driverClassName Indică numele clasei driver pentru acces la baza de date.2.2. Atenţie! Pentru situaţiile în care se impune o parolă. 406 .2. fişierul server.• password Este parola de acces la bază. sau url (sunt echivalente) Indică string-ul de conectare la baza de date.xml trebuie bine securizat.

Sign up to vote on this title
UsefulNot useful