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

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

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

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

• 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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

2.mail.3. cunoscute şi sub numele de pachetul JavaMail. mail.<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. aceea oferită de către pachetete javax.19.4</OPTION> <OPTION VALUE="SQL Server">SQL</OPTION> </SELECT> </TD> <TD ALIGN="right">Operating System:</TD> <TD> <SELECT NAME="ddlb_os" size="1"> <OPTION VALUE="W2k">Windows 2000</OPTION> <OPTION VALUE="Linux">Linux</OPTION> <OPTION VALUE="Solaris">Solaris</OPTION> </SELECT> </TD> </TR> </TABLE> </CENTER> <BR>Continutul mesajului: <BR><TEXTAREA NAME="txtProblem" COLS="50" ROWS="4"> </TEXTAREA><HR><BR> <CENTER> <INPUT TYPE="Submit" NAME="btnSubmit" VALUE="Trimite mesajul"> </CENTER> </FORM></BODY></HTML> Programul 7.1. Clasa Mailer.mail şi javax.html 7.internet. Ne vom opri doar la una dintre modalităţi. 379 . expeditor de mesaje email Trimiterea mesajelor e-mail din Java se poate face în mai multe feluri.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Sign up to vote on this title
UsefulNot useful