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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Figura 7.15. Numărarea parametrilor prin cookies 371 .

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

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

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

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

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

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

3.19.1.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. <HTML><HEAD><TITLE>Compunere de mesaj</title></HEAD> <BODY> <FORM ACTION="http://florin. Formular de trimitere a unui mesaj Documentul HTML de descriere a acestui formular este dat în programul 7. Intrarea se face printr-un formular prezentat în fig.16. Figura 7. Clientul mail.1.ubbcluj.cs. 7.7.

mail.1.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. expeditor de mesaje email Trimiterea mesajelor e-mail din Java se poate face în mai multe feluri. 379 .internet.html 7.<TD><INPUT TYPE="Text" NAME="txtFirst" ALIGN="LEFT" SIZE="15"></TD> <TD ALIGN="right">Prenumele:</TD> <TD><INPUT TYPE="Text" NAME="txtLast" ALIGN="LEFT" SIZE="15"></TD> </TR> <TR> <TD ALIGN="right">Email:</TD> <TD><INPUT TYPE="Text" NAME="txtEmail" ALIGN="LEFT" SIZE="25"></TD> <TD ALIGN="right">Telefon:</TD> <TD><INPUT TYPE="Text" NAME="txtPhone" ALIGN="LEFT" SIZE="15"></TD> </TR> <TR> <TD ALIGN="right">Software:</TD> <TD> <SELECT NAME="ddlb_software" SIZE="1"> <OPTION VALUE="Word">Microsoft Word</OPTION> <OPTION VALUE="Java">Java SDK1.mail.3.19. Ne vom opri doar la una dintre modalităţi. aceea oferită de către pachetete javax.mail şi javax.2. Clasa Mailer. cunoscute şi sub numele de pachetul JavaMail.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Sign up to vote on this title
UsefulNot useful