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

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

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

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

• 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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Sign up to vote on this title
UsefulNot useful