. Generarea Dinamica a Paginilor Web Folosind Tehnologia Servlet
Introducere
Punctul de pornire al acestei lucrări este următorul: am dorit să realizez o pagină Web pentru o instituție (de ce nu școala unde îmi desfășor activitatea de cadru didactic). Datorită faptului că mulți foști elevi ai școlii revin după un timp și doresc anumite adeverințe sau extrase din dosarul personal pentru a atesta calitatea lor de absolvenți ai unei forme de învățămînt, m-am gîndit că ar fi utilă și practică ,,îmbogățirea” paginii Web cu o facilitate de căutare puțin mai avansată. Astfel din pagina Web clientul poate folosi o aplicație care permite prin completarea unui formular (totală sau parțială) obținerea fișei personale sau a dosarului unui fost elev, sau profesor al școlii. Mai precis, am dorit ca răspunsul către client să fie sub forma unui document HTML virtual, deci o pagină Web care să conțină date utile extrase dintr-o bază de date (arhiva școlii): numele și prenumele complet, data nașterii, domiciliu, grupa sanguină, date despre părinți, anul absolvirii, mediile de absolvire, fotografie, caracterizare psihipedagogică, coeficient IQ, etc. O parte dintre aceste date vor fi confidențiale – nu vor apare în documentul răspuns, ele putînd fi accesate doar de web designer, directorul școlii, sau eventual de către secretară și numai după introducerea unei parole. De asemenea tot aceste persoane pot avea acces la baza de date electronică pe care o pot actualiza la nevoie.
Înainte de a prezenta însă produsul final (aplicația prezentată mai sus) am considerat util a prezenta drumul pînă la alegerea soluției optime (cel puțin din punctul meu de vedere). Lucrarea prezintă așadar o parte (atît cît a studiat autorul) din tehnologiile și standardele Web actuale, precum și unele considerații asupra gradului de eficiență și fiabilitate a acestora.
Lucrarea se adresează celor care dețin cunoștințe elementare de programare C++, Java, HTML, și operare Unix, dar nu numai. Cititorii neavizați (bubulici) pot dobîndi aceste cunoștințe consultînd bibliografia propusă.
Mulțumesc domnului profesor coordonator Corneliu Sabin Buraga, domnului profesor Ștefan Andrei, dar nu în ultimul rînd colegilor care m-au ajutat cu sfaturi, aprecieri critice, sau exemple menite să faciliteze lectura și însușirea cunoștințelor expuse în lucrare (Paul Cezar Coca, Ionuț Bucur și mulți alții cu care am păstrat legătura prin chat sau e-mail).
CGI (Common Gateway Interface)
Este unul dintre primele standarde de interfață pentru interacțiunea clienților Web cu serverele Web și se află în prezent la versiunea 1.1. Un program CGI, numit în mod uzual script, se execută pe serverul Web, fie în mod explicit, fie la preluarea informațiilor aflate în cadrul cîmpurilor unui formular interactiv sau coordonatele unei zone senzitive (image map). Acest standard oferă interactivitate paginilor Web, documentele HTML putînd astfel să-și modifice în mod dinamic conținutul și să permită prelucrări sofisticate de date. Programarea CGI, apărută la începutul anului 1995, a transformat Web-ul dintr-un simplu sistem de consultare și aducere de fișiere într-o platformă în care clienții pot instala aplicații Web de mare utilitate, oferind totodată posibilitatea acestora de a interacționa cu site-urile Web, sau de a transfera date, de a consulta baze de date, de a construi contoare de acces a documentelor, etc. În concluzie înainte de introducerea standardului CGI, Web-ul era, de fapt, folosit numai pentru distribuirea de documente statice: text și imagine. Odată cu apariția acestuia, serverele Web au putut fi conectate pentru a procesa informația în mod dinamic. Tocmai de aceea CGI este un standard pentru intefețele aplicațiilor externe ce extind funcționalitatea serverelor Web cu ajutorul căruia serverul Web poate apela un program, îi poate transfera acestuia datele introduse de utilizator (eventual printr-un formular dintr-o pagină Web), poate prelucra rezultatele pentru a le trimite înapoi utilizatorului.
Programele CGI pot fi scrise în multe limbaje, fiind interpretate (în cazul Perl, scripturi shell
-e.g. bash) sau compilate (C, C++, Delphi, etc).
Cele mai multe scripturi CGI sunt concepute pentru a procesa datele introdusele în formularele dintr-o pagină Web (vezi anexa 2).
Să presupunem umătorul exemplu în care utilizatorul introduce prin intermediul unui formular două numere întregi , iar programul CGI va genera o pagină Web conținînd suma lor. Formularul HTML va fi :
<form action=http://www.infoiasi.ro/cgi-bin/suma.cgi method=”GET”>
<p>Vă rugăm introduceți două numere: </p>
<input name=”nr1” size=”5”/>
<input name=”nr2” size=”5”/>
<p><input type=”submit” value=”Află suma”/></p>
</form>
De exemplu introducînd numerele 4 și 7 în cîmpurile corespunzătoare și acționînd butonul cu eticheta ,,Află suma”, vom primi o pagină Web – document HTML – care va afișa ,,Suma dintre 7 și 4 este 11”.
Pentru o mai bună înțelegere descriem mai jos mecanismul prin care funcționează interfața CGI.
Presupunînd că în cele două cîmpuri ale formularului (purtînd numele nr1 și respectiv nr2) am introdus valorile 7 și respectiv 4 și am apăsat butonul submit, navigatorul – browserul – va trimite, prin intermediul protocolului HTTP o cerere către serverul Web la adresa dată de URL-ul http://www.sc_baltati.ro (adresa este preluată din valoarea atributului <form>). Dacă în loc de o adresă absolută se specifica o cale relativă ar fi însemnat faptul că se folosește serverul pe care este găzduită pagina Web conținînd formularul. În general cererile clienților sunt însoțite de date care trebuiesc prelucrate (este și cazul exemplului nostru). În cazul cînd aceste date sunt trimise prin intermediul formularelor există două metode prin care ele sunt trecute serverului: POST și GET (vom detalia pe parcurs). Atunci cînd datele sunt preluate de server prin metoda GET, în momentul trimiterii cererii HTTP, navigatorul construiește un URL a vînd ca sufix informații despre datele introduse în cîmpurile formularului, folosind o codificare specială; în cazul nostru http://www.sc_baltati.ro/cgi-bin/suma.cgi?nr1=7&nr2=4. Pentru fiecare cîmp al formularului se va genera o pereche nume_cimp=valoare delimitată de caracterul ,,&”, iar caracterele speciale (de exemplu slash-ul sau apostroful ) vor fi înlocuite în codul lor numeric, în hexazecimal, precedat de caracterul ,,%”. Spațiile vor fi substituite cu semnul ,,+”. Serverul spre care a fost trimisă cererea va procesa datele recepționate conform unor reguli proprii. Tipic configurația serverului definește unde sunt stocate în cadrul sistemului de fișiere directoarele și fișierele CGI. De obicei în cazul unui daemon HTTP – adică server Web – fișierele sale sînt stocate în directotul /home/httpd (în cazul unei platforme Unix/Linux). Aici, alături de directotul html, unde se memorează documentele HTML ale unui site Web, se află și directorul cgi-bin, unde ar trebui să fie stocate toate fișierele (scripturile) CGI apelate din cadrul paginilor Web. Așadar http://www.sc_baltati.ro/cgi-bin/suma.cgi va însemna pentru serverul Web de la adresa http://www.infoiasi.ro următoarea acțiune: ,,invocă programul suma.cgi aflat la adresa /home/httpd/cgi-bin”. Astfel în loc să trimită către navigatorul Web care a inițiat cererea un document HTML sau un fișier de alt tip un server va invoca scriptul CGI specificat în cadrul URL-ului (în acest caz suma.cgi) și-i va pasa acestuia datele furnizate de sufix după semnul ? (adică șirul nr1=7&nr2=4). Pentru formularele care utilizează metoda GET (metodă implicită) specificațiile CGI indică faptul că datele furnizate scriptului vor fi preluate (disponibile) într-o variabilă de mediu denumită QUIERY_STRING. Scriptul CGI în mod uzual va furniza datele de ieșire care vor fi trimise navigatorului Web de către server, utilizîndu-se ieșirea standard stdout.
Să vedem acum un exemplu de script CGI (scris folosind limbajul C++) care va procesa datele din formularul nostru prin metoda GET.
\#include <stdio.h>
#include <stdlib.h>
/*setăm tipul MIME pentru antetul trimis de programul CGI serverului*/
#define CONTENT “Content-type: text/html\n\”
int main(void)
{
char *data;
long int nr1, nr2;
/*obligatoriu trimite tipul de date (în format MIME)*/
printf(CONTENT);
/*Antetul HTML*/
printf(“<html>\n<head><title>Rezultat</title></head>\n”);
/*Corpul documentului*/
printf(“<body bgcolor=\”white\” text=/”black\”>/n”);
/*procesează datele folosind funcția getenv() care preia datele din QUIERY_STRING*/
data=getenv(“QUIERY_STRING”);
if (data=NULL)
printf(“<p>Eroare la procesarea datelor</p>\n”);
else
if (sscanf(data, “nr1%ld&nr2=%ld”,&nr1, &nr2)!=2);
printf(“<p>Eroare! Datele trebuie să fie numerice!</p>”);
else
printf(“<p>Suma dintre %ld și %ld este%ld.<p>\n”,nr1, nr2, nr1+nr2);
/*terminare*/
printf(“</body>\n</html>\n”);
return 0;
}
Datorită faptului că avem un format fix al datelor recepționate am putut folosi funcția sscanf(), dar pentru decodificări mai complexe va trebui să construim funcții speciale. După compilare (cu gcc în mediile Unix/Linux), executabilul va trebui plasat în directorul cgi-bin al serverului Web. Pentru a putea fi accesat, va fi necesar să fie executat de proprietar, de grup și de alții (drepturile 755 în Unix). După instalarea scriptului orice utilizator, dacă nu sunt impuse restricții de către administratorul Web, îl va putea accesa și executa prin intermediulunui formular HTML sau explicit. Astfel scriptul trebuie să fie capabil să proceseze orice tip de dată și să semnaleze eventualele erori.
Prezentăm în continuare cum se procesează datele recepționate printr-un formular dar preluate prin metoda POST și folosind mecanismul SSI.
Să presupunem că avem situația următoare: este necesar ca prin intermediul unui formular să preluăm de la utilizator o linie de text care va fi trimisă unui script CGI să o adauge la sfîrșitul unui fișier stocat pe server. Acest fișier va putea fi modificat doar de autorul formularului și de script și va putea fi citit de oricine altceva prin intermediul altui script. Dacă în exemplu anterior (cel cu scriptul care realiza suma a două numere) nu realizam decît o simplă prelucrare de date care nu afecta deloc starea serverului (nu se modifică nici un fișier de pe server) și cererea de aflare a sumei a celor două numere putea fi trimisă ori de cîte ori fără a genera probleme, în acest caz prin intermediul scriptului vizat se va modifica conținutul unui fișier deci și starea serverului. Pentru aceasta vom folosi metoda POST. Ca și în cazul metodei GET scriptul CGI are acces la întregul mediul sistemului de operare pe care rulează.. Metoda POST este utilă pentru cazurile în cate avem de trimis scriptului CGI spre prelucrare un volum mai mare de date (de exemplu conținutul unei scrisori introduse printr-o <textarea>, ori al unui fișier prin acțiunea de upload)sau informații confidențiale (parole) care nu trebuie să apară în componența URI-ului trimis serverului Web. Pentru formularele utilizînd metoda POST, datele trimise scriptului vor putea fi accesate de la intrarea standard stdin, iar lungimea în octeți, a datelor trimise va fi disponibilă prin varibila de mediu CONTENT_LENGTH. Vor trebui citite atîtea caractere cîte arată variabila de mediu CONTENT_LENGTH, nu mai multe. Formularul prin care se solicită linia ce va fi adăugată în fișier este:
<form action=http://www.infoiasi.ro/cgi-bin/addproj.cgi method=”POST”>
<p>Introduceți titlul proiectului: </p>
<input name=”data” size=”50” maxlength=”80” />
<input type=”submit” value=”Trimite” />
</form>
Programul C++ (sriptul CGI) care acceptă datele și le adaugă fișierului proj.txt este dat mai jos:
#include <stdio.h>
#include <stdlib.h>
#define MAXLEN 80
#define EXTRA 5
/*4 pentru numele cîmpului “data”, 1 pentru “=” */
#define MAXINPUT MAXLEN+EXTRA+2
/*1 pentru sfîrșit de linie, 1 pentru NULL */
/* numele fișerului (cale relativă) */
#define DATAFILrului (cale relativă) */
#define DATAFILE “proj.txt”
/*funcția de decodificare a datelor pasate programului CGI */
void unencode(char *src, char *last, char *dest)
{
for(;src!=last, last++, dest++)
if(*src=’+’) /*înlocuiește “+” cu spațiu */
*dest=’ ‘;
else
if(*src=’%’) /*înlocuiește codul hexa cu caracterul corespunzător */
{
int code;
if(sscanf(src+1, “%2x”, &code)!=1)
code=’?’;
*dest=code;
src=+2;
}
else *dest=*src;
*dest=’\n’;
*++dest=’\0’;
}
/*funcția principală */
int main(void)
{
char *lenstr;
char input[MAXINPUT], data[MAXINPUT];
long len;
/* scrie Content-type urmat de setul de caractere (opțional), în acest caz ISO-8859-2 (vom folosi diacritice) */
printf(“%s%c%c\n”, “Content-type: text/html;charset=iso 8559-2,13, 10);
/*scrie antetul documentului HTML generat */
printf(“<html>\n<head><title>Răspuns</title></head>\n”);
/*ia numărul de caractere transmise */
lenstr=getenv(“CONTENT_LENGTH”);
/*numarul de caractere este corect */
if(lenstr==NULL||sscanf(lenstr, %ld, &len)!=1||len>MAXLEN)
printf(“<p>Eroare – formular incorect?\n”);
else
{
FILE *f;
/*citește de al intrarea standard datele */
fgets(input, len+1, stdin);
/*decodifică linia */
unencode(input+EXTRA, input+len, data);
/*adaugă în fișier */
f=fopen(DATAFILE, “a”);
if(f==NULL) printf(“<p>Eroare la adăugarea în fișier</p>\n”);
else
{
fputs(data, f);
fclose(f);
printf(“<p>Mulțumim! Linia <b>%s</b> a fost adăugată
în fișier\n”,data);
}
/*sfîrșitul documentului */
printf(“</body>\n</html>\n”);
return 0;
}
Mecanismul SSI (Server Side Includes) oferă posibilitatea execuției de programe CGI și de efectuarea altor acțiuni direct din cadrul paginii Web prin intermediul unor comenzi incluse în codul HTML, fără a necesita prezența unui formular pentru a activa scriptul CGI dorit. Fiecare comandă SSI va putea fi introdusă printr-un comentariu special <!-#command – >, unde command este o comandă suportată de specificația SSI. Din moment ce este inclusă ca un comentariu HTML, navigatorul Web o va ignora, dar va fi procesată pe partea de server și comentariul va fi înlocuit cu ieșirea acelei comenzi. Fișierele HTML care uitlizează comenzi SSI vor fi salvate cu extensia .shtml în loc de .html, dar această convenție nu este obligatorie. Prezentăm în continuare ca exemplu doar comanda SSI exec (pentru mai multe amănunte recomandăm lucrarea [5]). Comanda este una dintre cele mai folosite comenzi SSI și invocă un program CGI pe partea de server, iar rezultatul va fi plasat în locul invocării comenzii exec .Programul CGI va trebui să aibă setate permisiunile de execuție. Să luăm cazul unui program CGI care alege aleatoriu un citat celebru pentru a fi afișat la fiacare încărcare a unei pagini Web. Pentru a-l invoca va trebui să inserăm în cadrul documentului HTML următoarele (în care citate.cgi este numele scriptului CGI conceput care afișează la ieșirea standard un citat ales dintr-o serie de citate stocate pe server – pentru exemplificare vezi www.cs.tuiasi.ro):
<html>
<head><title>Pagina ta </title></head>
<body>
<h5 align=”right”>
<i>Citatul zilei: </i><br/>
<!/#exec cgi=”citate.cgi”->
</h5>
<hr/>
…..
</body></html>
Un alt tip de aplicație creată folosind standardul CGI este un program gateway, care este un script folosit pentru a accesa informația care nu este direct accesibilă clientului: de pildă informația dintr-o bază de date. Scripturile de acest fel se scriu în limbaje ce posedă extensii pentru a formula interogări SQL și pentru a citi informația conținută în bazele de date (vezia nexa 3).
O a treia aplicație care se poate dezvolta prin programare CGI o reprezintă documentele virtuale, care sunt create ca răspuns la cererea clientului. Se poate crea astfel un document HTML oricît de complex conținînd text, imagine, fișiere de sunet sau video.
Totuși programele CGI prezintă și o serie de dezavantaje:
Serioase probleme de scalabilitate și performanță: fiecare cerere CGI lansează un nou proces pe server, iar dacă mai mulți utilizatori fac acest lucru simultan, mare parte din resursele serverului Web sunt astfel epuizate, efectul imediat fiind că se încetinește aplicația – cu efecte neplăcute pentru utilizator.
Probleme de securitate: datorită ușurinței cu care sunt concepute și scrise programele CGI, programatorii le tratează cu aceeași ușurință ca pe niște programe obișnuite, neglijînd faptul că scriptul respectiv poate afecta starea serverului Internet. Un atacator poate așadar specula bug-urile (deficiențele) din scripturile CGI stocate pe server pentru a obține acces neautorizat la fișierele sistem, sau chiar pentru a obține controlul întregului sistem. Programele CGI sunt scrise deseori pentru a accepta date într-un anume format, dar spre program, pot fi trimise date arbitrare de lungime nelimitată. Asta înseamnă că aceste programe trbuie scrise robust, astfel încît să fie capabile să refuze datele de intrare atunci cînd acestea sunt rău-intenționate sau bizare. Posibilitatea de a adăuga funcții unui server Web prin scripturile CGI complică foarte mult politica de securitate ce trebuie adoptată – atît timp cît un script CGI poate adăuga elemente la un server Web, pot fi și trebuie, introduse noi probleme de securitate (de exemplu un server Web poate fi configurat astfel încît să poată fi accesate numai fișiere dintr-un anume director, dar un utilizator poate instala neintenționat un script CGI care permite intrușilor să poată citi fișiere din alte directoare). Este posibil chiar ca scripturile CGI locale care conțin bug-uri să permită unui intrus să execute comenzi arbitrare pe sistemul în cauză (ținînd cont de faptul că nu toți utilizatorii au experiență în a scrie programe de securitate. În concluzie orice script CGI care rulează pe server trebuie să se aștepte la atacuri dese și să poată stăpîni orice încercare de intrare rău intenționată, furnizînd întotdeauna în aceste cazuri mesaje de eroare.
Programele CGI sunt lente și mai ales dacă sunt scrise într-un limbaj interpretat, lent, precum Perl și nu în cod compilat C.
Accesul direct la memorie: pointerii pot permite accesul direct la locațiile de memorie, iar dacă scriptul folosește pointeri atunci o eroare poate provoca blocarea sistemului deci și a serverului.
Probleme la portabilitate : pogramele CGI nu fac parte din categoria celor care ,,scrise odată, rulează oriunde”, adică nu sunt portabile – pentru a fi distribuite trebuie rescris codul pentru fiecare platformă Unix, Widows, Mac, etc.
Specificația completă pentru CGI o puteți găsi la adresa http://hoohoo.ncsa/uiuc.edu/cgi
Servlet-uri
Servelet-urile sunt componente software independente de platforma aplicațiilor server, care extind dinamic serverele care au suport Java integrat. Ele sunt independente și de protocol, asigurînd un cadru general pentru servicii pe baza modelului cerere-răspuns. Cu ajutorul servlet-urilor extindem deci funcționalitatea unei aplicații de tip server informațional – FTP, Telnet, SMTP,servere de știri,etc. (nu neapărat server HTTP) – un prim domeniu de aplicare (de altfel cel care ne interesează pe noi) fiind binențeles, extinderea serverelor Web. De exemplu cînd un browser trimite o cerere către un server, serverul o trimite mai departe către servlet. Servlet-ul procesează cererea (accesînd eventual o bază de date) și construiește un răspuns convenabil (de obicei în HTML) care este returnat clientului. Pentru ca un servlet să poată rula pe un server Web este nevoie ca serverul să aibă integrată o mașină virtuală Java (JVM – Java Virtual Machine), să dispună de o interfață de comunicare cu servlet-uri și să gestioneze servlet-urile aflate în execuție. Prima părere pe care ne-o putem face (numai după cele expuse mai sus) este că un servlet nu diferă cu nimic față de un script CGI – asigură acces prin Web la informații prezentate ca pagini HTML și nu numai, oferindu-ne posibilitatea de a vizualiza și de a modifica interactiv aceste date folosind tehnici de generare dinamică a paginilor Web. Pe parcurs însă vom face o diferență clară între cele două tipuri de programe sub multe aspecte. Dar iată pentru început care ar fi posibilitățile oferite de servlet-uri (o parte din acestea se regăseau și în cazul CGI –urilor):
Construiesc dinamic și returnează un fișier HTML pe baza cererii clientului.
Procesează datele completate de utilizatori printr-un formular HTML și returnează un răspuns, dacă este cazul.
Facilitează comunicațiile între mai multe grupuri de oameni prin publicarea de informații trimise de mai mulți clienți.
Furnizează suport pentru autentificarea utilizatorului și alte mecanisme de securitate.
Interacționează cu resursele server-ului, cum ar fi bazele de date, fișiere cu informații utile pentru client.
Procesează intrările de la mai mulți clienți pentru aplicații cum ar fi jocurile în rețea.
Permite server-ului să comunice cu un applet client printr-un protocol specific și păstrează conexiunea în timpul ,,conversației”.
Atașează automat elemente de design pentru pagini Web, cum ar fi antete, note de subsol, casete cu citate, etc., pentru toate paginile returnate de server (similar cu mecanismul oferit de SSI).
Forward-ează cereri de la un server la altul în scop de echilibrare al încărcării (load– balancing). De asemenea un server poate conecta între ele mai multe servlet-uri într-un lanț de filtre, fiecare servlet din lanț putînd face o modificare sau procesare individuală a datelor de intrare. Servlet-urile pot fi o componentă intemediară a unei aplicații distribuite, putînd fi la rîndul lor clienți ai unor alte servici scrise în alt limbaj. Putem construi deci cu ajutorul servlet-urilor aplicații client-server (vezi Anexa1). Bazate pe modelul de trei nivele (three tier arhitecture). Primul nivel poate fi un applet, servlet-ul fiind componenta de mijloc (middle tier), iar componenta a treia o bază de date. În acest caz servlet-ul folosind interfața JDBC (Java DataBase Conectivity), accesează o bază de date relațională.
Plecînd de la ideea că servlet-urile sunt folosite pentru extindere dinamică, le putem folosi ca pe niște componente auxiliare de tip plug-in, de exemplu în cazul unor motoare de căutare.
Se poate spune că servlet-urile sunt un hibrid între CGI și interfețele de programare de nivel inferior ca NSAPI sau ISAPI. Un servlet se comportă ca un script CGI, dar are o durată de viață lungă și se încarcă dinamic (ca și modulele NSAPI/ISAPI), eliminîndu-se astfel întîrzierile de la pornire specifice CGI-urilor. După încărcare, la o nouă cerere, nu mai este necesar să fie creat un nou servlet – serverul doar apelează o metodă care deservește cererea, astfel încît este nevoie doar de o schimbare de context ușoară (light-weight) pentru alt fir de execuție (vom detalia pe parcurs). Fiind scrise în Java, servlet-urile ,,moștenesc” o mare parte din caracteristicile și punctele tari ale limbajului:
Portabilitatea – poate rula pe diverse platforme fără a necesita o rescriere sau o recompilare a codului.
Încărcarea dinamică – poate fi încărcat și invocat de pe discul local sau de la distanță, prin rețea.
Configurabilitatea – instalarea, pornirea, oprirea, specificarea parametrilor de start și a caracteristicilor unui servlet sunt realizate într-un mod simplu și sugestiv, prin intermediul unui sistem de administrare ce dispune de o interfață grafică (GUI – based Administration Tool).
Siguranță în funcționare – modelul de securitate și încapsularea (sandbox) protejează servlet-ul de atacuri, chiar și în momentul cînd este apelat de la distanță. Deoarece încărcarea în memorie a servlet-urilor se face prin mecanismul standard Java (folosit de orice altă mașină virtuală Java), care permite încărcarea claselor Java din rețea, apar anumite probleme de securitate. Aceste probleme se referă în primul rând la servlet-urile aduse din rețea, de pe alte servere. În API –ul Servlet există suport integrat pentru securitate. Acest lucru se datorează de fapt mediului Java standard care are un Security Manager ce poate controla accesul la toate resursele, cum ar fi fișierele locale, alte fire care rulează pe același server, informații administrative ale serverului (utilizatori, grupuri) sau accesul la alte servere din rețea. Prin acest mecanism se poate asigura un mediu de execuție controlat (Sandbox) pentru servlet-uri similar cu cel folosit de navigatoarele Web pentru controlul applet-urilor. Administratorul serverului poate specifica deci, prin intermediul Security Manager-ului, cărui servlet să i se acorde drepturi de accesare a rețelei sau a fișierelor locale. Dacă luăm în considerare recent apărută posibilitate de a semna digital fișiere Java Archive (JAR), care conțin clase Java arhivate, am putea considera următoarea politică de securitate:
Din moment ce sunt instalate local de către administratorii serverului, servlet-urile locale au acces deplin la toate resursele.
Toate servlet-urile din rețea se execută în interiorul sandbox-ului. Servlet-urile care sunt semnate digital și sunt pe o listă de semnături în care administratorul are încredere, au acces deplin la toate resursele. Servlet-urile semnate, dar care nu sunt pe listă, sunt considerate ca fiind fără semnătură.
Servlet-urile care nu sunt semnate digital se execută în sandbox și nu au acces la nici o resursă
Binențeles această soluție poate fi extinsă, putînd fi adoptată o politică mai flexibilă. De exemplu pentru servlet-uri cu o anumită semnătură putem permite accesul doar la rețea sau la anumite fișiere.
Posibilitatea de înlănțuire – un servlet poate invoca unul sau mai multe servlet-uri într-o secvență.
Execuția dinamică – poate fi apelat direct din paginile Web utilizînd tag-uri recunoscute de server.
Existența unui API (Interfețe de proiectare a aplicațiilor) standard – utilizează Servlet API, care este o extensie a API-ului standard Java; astfel un servlet este independent de protocolul de rețea utilizat, de modul în care este încărcat sau de serverul care-l rulează. Odată cu lansarea limbajului java, firma Sun a oferit programatorului și un pachet complet de API-uri pentru principalele funcții de care are nevoie o aplicație completă: interfață grafică, multithreading, operații matematice, comunicare în rețea, acces hardware,etc. Majoritatea acestor API-uri sunt complet implementate în Java, dar există și o serie de API-uri direct legate de hardware, din această cauză ele fiind scrise în limbaje compatibile în cod nativ și care trebuiau reimplementate pentru fiecare platformă. Totuși în ultimii ani Sun a făcut eforturi foarte mari ca să poată oferi o nouă generație de API-uri care să dea aplicațiilor Java posibilitatea de a accesa plăci de sunet, să comunice pe linii seriale, să acceseze cît mai mult dintre facilitățile plăcilor video, etc. În afară de aceste API-uri Sun a dezvoltat și restul API-urilor standard și a introdus altele noi , pentru a oferi proiectanților și programatorilor Java cît mai multă funcționalitate prefabricată. Facem în continuare o trecere în revistă acelor mai importante Api-uri oferite proiectanților de servlet-uri:
AWT (Abstract Window Toolkit) – a fost prima generație de API-uri destinate construcției de interfețe grafice utilizator. Acest API a fost ulterior înlocuit cu un altul, mai elaborat, numit Swing, care de fapt este o rescriere a vechiului standard pe principii profund îmbunătățite și care oferă funcționalitățile necesare unei interfețe moderne. Din păcate Swing rămîne încă foarte lent și de aceea se pot găsi încă foarte multe aplicații Java care continuă să folosească vechiul AWT;
specificația Java Beans – care a definit începînd cu JDK v1.1 modul în care pot fi create componentele software pentru mediile de dezvoltare Java și cum se pot defini evenimente, metode și proprietăți ale acestora (vezi lucrarea [2]). În majoritatea cazurilor, când este realizată o aplicație, ceea ce se dorește cu adevărat este obținerea de componente care să îndeplinească anumite funcții. În continuare, ar fi interesant ca aceste module să poată fi asamblate după o schemă, așa cum un electronist montează chip-uri pe o placă (sau, în cazul Java, într-o pagină Web). Pentru a rezolva această problemă, a fost introdus conceptul de programare vizuală, în care componentele sunt prezentate grafic, într-o formă asemănătoare cu cea pe care o vor avea în program. Astfel, procesul de programare se transformă într-un proces de alegere de componente dintr-o paletă și asamblarea lor utilizând un editor vizual. Sistemul de dezvoltare va genera cod pentru aceste componente și astfel ele vor apărea în programul final.
Simpla punere împreună a componentelor nu este în mod normal suficientă pentru a obține un program. Cel mai adesea, va fi nevoie ca anumite atribute ale componentei să poată fi schimbate (de exemplu, culoarea, textul afișat, baza de date la care trebuie să se conecteze etc.). Caracteristicile care pot fi modificate în momentul proiectării poartă numele de proprietăți. Pe lângă acestea, o componentă are și un set de comportamente, reprezentate prin evenimente (specifică modul în care trebuie să reacționeze). Partea cea mai dificilă în realizarea unui mediu de programare vizuală îi revine uneltei de dezvoltare care trebuie să fie capabilă să interogheze dinamic o componentă pentru a afla proprietățile și evenimentele pe care le suportă (procedeul se numește introspecție). Proprietățile pot fi modificate (vor fi salvate la compilare), iar unui eveniment i se poate asocia o secvență de cod ce va fi executată când acesta se produce.
În această idee, au fost create medii de dezvoltare vizuale precum VisualBasic (VB) de la Microsoft, urmat de Delphi de la Borland (care a fost prima inspirație la proiectarea JavaBeans).
Pentru a crea o componentă VB, trebuie scris însă un cod destul de complicat, care urmează anumite convenții pentru găsirea proprietăților și evenimentelor. Delphi, care face parte din a doua generație de instrumente de programare vizuală, este proiectat pentru dezvoltarea vizuală ușurând procesul de manipulare al componentelor. Java reușește să desăvârșească procesul de creare al componentelor vizuale prin JavaBeans.
JavaBeans este o facilitate introdusă de JDK 1.1, și face posibilă scrierea de componente software reutilizabile (pe scurt beans), care pot fi manipulate vizual. Pentru a realiza un bean, nu trebuie scris cod suplimentar și nu sunt necesare extensii speciale ale limbajului. De fapt, un bean (bob) este o clasă normală Java care folosește o convenție de numire a metodelor. Pe baza numelui metodei, un editor vizual își poate da seama dacă are de-a face cu o proprietate, un eveniment sau o metodă normală. Crearea de servlet-uri care sunt în același timp și bean-uri oferă două avantaje distincte:
– orice modificare a configurației servlet-ului este efectuată imediat;
– persistența stării, precum și configurația unui servlet de tip bean pot fi salvate într-un fișier prin serializare (de exemplu, în cazul unui contor, valoarea sa poate fi salvată și apoi refăcută la oprirea și, respectiv, pornirea server-ului prin folosirea mecanismului Java de serializare).
Implicit, server-ul Java consideră orice servlet ca fiind bean și-i inițializează (prin introspecție) toate proprietățile primite ca argumente. De exemplu, un contor ar putea primi ca argument valoarea inițială ("initial=0") sau un servlet cu numele "CGI" ar putea avea nevoie de directorul de start ("bindir=cgi-bin"). Servlet-urile de tip bean pot fi distribuite sub formă de clase sau/și fișiere serializate ("nume_servlet.ser"). Un fișier serializat poate fi privit ca o instanță a unei clase. Împreună cu clasa, el poate fi pus într-o arhivă de tip JAR (poartă numele de fișiere instalate) sau poate fi lăsat separat (neinstalat). În ambele cazuri, server-ul este responsabil cu încărcarea și deserializarea servlet-urilor.
În continuare, urmează un exemplu de servlet bean HTTP care formează o pagină HTML ce conține un mesaj (implicit este "Salut!"). Acest mesaj poate fi modificat-fără a recompila clasa-cu ajutorul utilitarului de administrare al server-ului Java sau prin trimiterea unui argument de inițializare către servlet (printr-un fișier SayHi.initArgs, ce conține o pereche parametru-valoare – de exemplu, "message=La revedere!").
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class SayHi extends HttpServlet{
String message = "Salut!"; // o proprietate
// metodele "get" și "set" ale proprietății message
public String getMessage(){
return message;
}
public void setMessage( String message ){
this.message = message;
}
// această metodă va fi executată la fiecare //apel al servlet-ului (HTTP GET)
public void doGet( HttpServletRequest req, HttpServletResponse res )
throws ServletException, IOException{
PrintWriter out;
res.setContentType( "text/html" ); // tipul documentului
out = res.getWriter();//obține un stream către client
// formează o pagină HTML
out.println( "<html>" );
out.println( "<head><title>Say Something</title></head>" );
out.println( "<body>" );
out.println( "<h1>" + getMessage() + "</h1>" );
out.println( "</body> </html>" );
}
RMI (Remote Method Invocation) – interfață suport pentru crearea, regăsirea și accesarea de obiecte distribuiteîn rețea și pentru comunicarea între aceste obiecte.
Interfețele de programare Java Sevlet API dezvoltate de JavaSoft nu fac parte din nucleul deprogramare Java, fiind un standard API de extensie Java . Este dieci un pachet adițional pentru nucelul Java.
Ușurința în dezvoltare – mediul de dezvoltare JSDK (Java Servlet Development Kit) pune la dispoziția programatorului instrumente ce facilitează scrierea de noi servlet-uri.
Persistența și configurarea ,,on-the-run” – pentru crearea de servlet-uri se poate utiliza tehnologia Java Beans.
Servelet-urile pot fi considerate echivalentul applet-urilor (reamintim că un applet este o componentă software pe partea clientului care rulează în cadrul unui browser Web) pe partea de server, ele fiind asemănătoare din multe puncte de vedere: din punct de vedere al clientului (browser-ul) ambele sunt obiecte – bytecode ce pot fi încărcate dinamic din rețea (Internet) și executate pe mașina locală. Însă principala caracteristică a appleturilor, interfața grafică utilizator (GUI) lipsește din servlet-uri, ele neavînd nevoie de așa ceva din moment ce rulează în interiorul serverelor.
Deci, după cum am spus mai sus, servlet-urile sunt bazate pe modelul de programare cerere-răspuns (acceptare de cereri și generare de răspunsuri). Dar ca că poată recepționa cereri și să poată răspunde la ele, orice servlet este un obiect Java care se conformează unei interfețe speciale – adică în termenii programării Java, implementează interfața Servlet. Acest lucru se poate face direct, prin implementarea tuturor metodelor interfeței, sau indirect, prin moștenire, fie de la o clasă servlet generică (Generic Servlet), fie de la clasa servlet specifică servlet-urilor bazate pe protocolul HTTP (HttpServlet extinde de fapt și ea clasa generică). A doua soluție este mai des utilizată de proiectanți, deoarece în cel două clase sunt deja implementate metodele interfeței, iar programatorul suprascrie (override) doar metodele de care are nevoie. În interfața Servlet cea mai importantă metodă este Service (ServletRequest req, ServletResponse res). Această metodă este apelată ca răspuns la fiecare cerere recepționată din partea clientului. Cel mai simplu servlet posibil definește doar această metodă service, care are ca parametri două obiecte, instanțe ale claselor ServletRequest și respectiv ServletResponse. Primul parametru încapsulează datele trimise de client, fiind astfel posibil accesul servlet-ului la parametrii cererii – numele parametrilor trimiși de client, protocolul (schema) utilizat de client, numele (adresa) serverului care a trimis cererea și stream-ul de intrare ServletInputStream, prin intermediul căruia servlet-ul primește date de la clienți. Interfața service() furnizează servlet-ului metode pentru a controla lungimea conținutului și tipul MIME al răspunsului și accesul la stream-ul de ieșire, ServletOutputStream. Mai jos dăm un exemplu de servlet simplu care implementează interfața Servlet extinzînd clasa GenericServlet. Este definită o singură metodă, service(), care este executată la fiecare apel al servlet-ului, rezultatul fiind afișarea datei și orei curente folosind stream-ul de ieșire.
public class DateServlet extends GenericServlet{
public void service( ServletRequest request, ServletResponse response )
throws ServletExceptionm IOException{
Date today = new Date();
ServletOutputStream out = response.getOutputStream();
out.println( today.toString() );
}
}
Pentru o mai bună înțelegere a modului în care ,,lucrează” un servlet prezentăm mai jos ciclul de viață al acestuia – mai exact procesul apelării de către server a unui servlet:
Serverul încarcă servlet-ul cînd acesta este cerut de client, sau la startarea serverului dacă așa impune configurația. Servlet-ul poate fi invocat local sau dintr-o locație de la distanță folosind o facilitate de încărcare a claselor Java. De exemplu putem avea:
Class c=Class.forName(“surseFLux.ServletUnU”);
Serverul creează o instanță a clasei servlet-ului pentru deservirea tuturor cererilor. Folosind fire de execuție multiple, cererile concurente pot fi deservite de o singură instanță a servlet-ului. De exemplu putem avea:
Servlet s=(Servlet) c.newInstance();
Servlet-ul este activat prin invocarea metodei init(), de către server; această metodă garantează că termină execuția înaintea procesării primei cereri a servlet-ului și este încărcată o singură dată, dar dacă serverul creează mai multe instanțe ale servlet-ului atunci metoda se apelează de fiecare dată pentru fiecare instanță. De asemenea metoda este apelată din nou în cazul în care servlet-ul este reîncărcat (acest lucru este posibil doar după ce servlet-ul a fost mai întîi oprit). În cadrul acestei metode se efectuează inițializărilede care este nevoie pentru funcționarea servlet-ului. Dacă există calcule și operații costisitoare care trebuie să se repete la fiecare cerere, este de preferat să se execute o singură dată la inițializare.
După ce a fost încărcat și inițializat, servlet-ul este capabil să răspundă cererilor, pe care le procesează cu metoda service. În momentul primirii unei cereri pentru servlet, serverul construiește un obiect ServletRequest sau HttpServletRequest din datele introduse în cererea clientului.De asemenea acesta construiește un obiect ServletResponse sau HttpServletResponse care furnizează metode pentru returnarea răspunsului. Tipul parametrului depinde dacă servlet-ul extinde GenericServlet sau respectiv HttpServlet. Fiecare cerere din partea unui client este tratată concurent, printr-un apel service() într-un fir de execuție separat (cu excepția cazului cînd servlet-ul implementează interfața SingleThreadModel). Metoda service() pentru servlet-uri HTTP poate apela o metodă specifică cum ar fi doGet() sau doPost(), trimițînd ca parametri obiectele construite – amintite mai sus. Așadar, la un moment dat, pot exista mai multe metode service() în execuție, care pot utiliza chiar date în comun; accesul la aceste date comune se face însă printr-un sistem de excludere mutuală, folosind modelul syncronized, pentru păstrarea consistenței datelor (vezi [2]).
Metoda service() prosesează cererea clientului prin evaluarea obiectului ServletRequest sau HttpServletRequest.
Apoi acesta răspunde folosind obiectul ServletResponse sau HttpServletResponse. Dacă serverul primește încă o cerere pentru acest servlet procesul se reia printr-un nou apel al metodei service().
De fiecare dată cînd containerul servlet-ului determină că un servlet trebuie descărcat (din motive de corecție sau dacă acesta a ,,căzut”), serverul apelează metoda destroy(), după terminarea firelor de execuție ale metodei service(), sau după terminarea limitei de timp definită de server. Servlet-ul poate starta dealocatorul de memorie (garbage collector).
Exemplul de mai jos conține un servlet generic care întoarce timpul curent de la server (vezi și exemplul de mai sus).
import java.servlet.*;
//Acest servlet întoarce timpul curent de la server
public class ServletTimp extends GenericServlet
{
//Serverul apelează metoda service() pentru a răspunde la cererile adresate servlet-ului
public void service(ServletRequest cerere,ServletResponse raspuns)
throws ServletException. Java.io.IOException
{
//Declarăm o referință către fluxul de ieșire al clientului
java.io.PrintWriter iesireClient=raspuns.getWriter();
//Tipărim timpul curent și data către fluxul de ieșire
iesireClient.println*new java.util.Date*());
}
}
Din punct de vedere al modalităților de invocare un servlet se asemănă cu CGI-urile, dar apar și modalități caracteristice numai servlet-urilor:
Indirect, pentru generarea dinamică de documente HTML; se utilizează cînd un client cere un document gestionat de un servlet (care în prealabil poate consulta o bază de date pentru a formula răspunsul): serverul primește cererea, caută în parametrii de configurare și, cînd descoperă că nu este vorba de un document static (aflat pe disc), ci de unul generat, trimite cererea către servlet-ul respectiv care o execută. Tehnica este asemănătoare cu cea folosită de către serverele Web tradiționale pentru a invoca scripturi CGI. Aceasta este și modalitatea de invocare pe care am considerat-o a fi potrivită pentru pagina Web construită, despre care am vorbit în Introducere.
Direct, din directorul Servlets/; un servlet aflat în directorul servlets/ (relativ la directorul rădăcină al serverului) poate fi invocat prin numele clasei, fără extensie, utilizînd protocolul și portul corespunzător. Dacă pentru un servlet HTTP se dorește transmiterea unor parametri inițiali, aceștia trebuie plasați într-un fișier cu numele servlet-ului și cu extensia .initArgs utilizînd sintaxa ,,nume=valoare”. Cînd un servlet este recompilat în acest director, noua versiune este automat încărcată de către server.
Indirect utilizînd filtre înlănțuite; pentru satisfacerea anumitor cereri, poate fi necesară invocarea mai multor servlet-uri (înlănțuite ordonat). Datele de la client sunt trimise ca intrare primului servlet din lanț, iar ieșirea ultimului servlet este întoarsă clientului. Fiecare servlet din lanț are ieșirea redirectată către ieșirea următorului (modelul pipe).
Indirect printr-un tag SSI; orice fișier cu extensia .shtml va fi analizat sintactic de către server. Dacă un tag SSI va fi găsit, serverul va rula servlet-ul corespunzător și va insera ieșirea generată de acesta în locul tag-ului. Sintaxa unui tag SSI este foarte asemănătoare cu cea a unui applet:
<SERVLET CODE=nume_servlet.class CODEBASE=http://host/dir
initParam1=val1 initParam2=val2>
<PARAM NAME=serviceParam1 VALUE=val3>
<PARAM NAME=serviceParam2 VALUE=val4>
…
</SERVLET>
Parametrul CODEBASE este opțional și specifică o adresă la distanță pentru servlet. CODE și CODEBASE pot fi înlocuiți cu parametrul NAME, care indică numele simbolic dat servlet-ului la instalare.
Indirect, folosind un ,,alias”; modul de apel în acest caz este același ca în cazul invocării directe, doar că locul unde se află servlet-ul poate fi diferit de cel unde rulează serverul. Serverul va transfera local clasa, o va instanția și apoi o va rula.
Utilitarul Servletrunner cunoaște doar modul direct de apel.
Deoarece majoritatea servlet-urilor sunt extensii ale unui server Web, care utilizează protocolul HTTP pentru a interacționa cu clienții, metoda cea mai uzuală folosită pentru dezvoltare este de a utiliza clasa specializată javax.servlet.HttpServlet. Un astfel de servlet poate suporta orice metodă HTTP (GET, POST, HEAD, etc.), poate redirecta o cerere către altă locație, poate trimite mesaje specifice protocolului, poate accesa parametrii trimiși prin formulare (forms) HTML (metoda HTTP, URL destinație al cererii). Clasa HttpServlet implementează interfața Servlet prin extinderea clasei GenericServlet. În mod implicit, un servlet scris prin extinderea clasei HttpServlet permite existența mai multor fire de execuție (threads) care execută concurent metoda service(). Dacă se dorește accesul exclusiv (cel mult o metodă service() se poate afla în execuție la un moment dat), servlet-ul trebuie să implementeze interfața SingleThreadModel (nu este necesară scrierea unor metode noi). Un servlet HTTP comunică întotdeauna cu clienții utilizând formatul MIME pentru datele de tip cerere/răspuns. Astfel, este posibilă recepționarea cererii într-un format oarecare și returnarea datelor, folosind o altă codificare corespunzătoare tipului de răspuns (specificată de către servlet). În funcție de interacțiunea HTTP conținută în cerere, metoda service() va invoca metoda corespunzătoare. Astfel, este suficient ca un servlet să implementeze doar metoda sau metodele specifice destinației sale. Acestea sunt prezentate în tabelul "Metode invocate de service()" . Pentru obținerea argumentelor trimise de client, se pot utiliza mai multe metode, în funcție de operația HTTP solicitată:
(a) pentru orice tip de cerere, se poate folosi metoda getParameterValues(), care întoarce valoarea parametrului transmis ca argument; lista de parametrii se află cu metoda getParameterNames().
(b) pentru cererile de tip GET, metoda getQueryString() întoarce șirul cu datele trimise de client.
(c) pentru cererile HTTP POST, PUT și DELETE, există două variante de citire: dacă se așteaptă date de tip text, se folosește un obiect BufferedReader întors de metoda getReader(); altfel, pentru date binare, se utilizează un obiect ServletInputStream obținut printr-un apel al metodei getInputStream().
În cadrul aceleiași cereri, poate fi folosită doar una dintre cele trei metode.
Pentru a răspunde clientului, obiectul HttpServletResponse furnizează două metode de transmitere a datelor în funcție de tipul lor: pentru text cu un PrintWriter obținut cu metoda getWriter() sau pentru date binare cu un ServletOutputStream întors de metoda getOutputStream().
Înainte însă de a avea acces la writer sau stream trebuie configurat antetul documentului HTTP, specificând tipul și lungimea conținutului, precum și modul de codificare. Un răspuns este considerat terminat în momentul în care writer-ul sau stream-ul sunt închise.
Pentru a vedea mai clar funcționalitatea unui server, vom prezenta mai departe două metode de utilizare a metodelor doGet()și doPost() (pentru exemplificare vom alege același exemplu cu suma a două double):
Presupunem că este scris următorul fișier HTML cu textul:
<html><head><title>Un exemplu de adunare</title></head>
<body>
<h1>Suma a doua double</h1>
<p>Dati cele doua numere</p>
<form method=”GET”
action=”http://localhost:8080/examples/servlet/SumaDoubleGet”>
<p>Nr.1: <input type=”text” name=”nr.1”/></p>
<p>Nr.2: <input type=”text” name=”nr.2”/></p>
<input type=”submit” value=”Trimite”/>
<input type=”reset” value=”Sterge”/>
</form>
</body>
</html>
Se observă că acest fișier HTML utilizează metoda GET. De exemplu sub Windows, folosind serverul Tomcat, plasăm în directorul C:\Tomcat\jakarta-tomcat\webapps\examples\
Web-inf\classes\ fișierul *.class asociat codului Java de mai jos
import javax.servlet.http.*;
import javax.servlet.ServletException;
import java.io.*;
public class SumaDoubleGet extends HttpServlet
{ //procesăm un formular HTML pentru citirea a două numere de tip double și furnizăm ca răspuns o pagină HTML
protected void doGet(HttpServletRequest cerere, HttpServletResponse raspuns) throws ServletException, IOException
{
String numar1=cerere.getParameter(“nr1”);
String numar2=cerere.getParameter(“nr2”);
Double nr1=0.0,nr2=0.0;
Double d1Temp=Double.valueOf(numar1);nr1=d1Temp.doubleValue();
D1Temp=Double.valueOf(numar2); nr2=d1Temp.doubleValue();
DoublesumaDouble=nr1+nr2;
PrintWriter iesire=raspuns.getWriter();
iesire.println(“<html><head>”);
iesire.println(“<title>Un exemplude adunare</title>”);
iesire.println(“</head><body>”);
iesire.println(“<h1>Suma celor doua numere double</h1>”);
iesire.println(“<p>Suma celor doua numere double este” + sumaDouble
+ “</p>”);
iesire.println(“</body></html>”);
iesire.close();//inchidem fluxul de iesire
}
}
Astfel, apelând documentul de mai sus, după completarea câmpurilor și apăsarea butonului Trimite, acesta va apela servlet-ul SumaDoubleGet apelând metoda doGet(), care va trimite către client o pagină Web, ce va afișa în principiu, suma celor două numere double citite în formular.
În cele ce urmează vom prezenta o metodă mai elegantă de a face suma celor două numere. Astfel vom vedea o funcționalitate mai mare a servlet-ului folosind ambele funcții doGet() și doPost():
import javax.servlet.http.*;
import javax.servlet.ServletException;
import java.io.*;
public class SumaDouble extends HttpServlet
{
//returnam un formular HTML pentru citirea a doua nr. De tip double
protected void doGet(HttpServletRequest cerere, HttpServletResponse
raspuns)throws ServletException, IOException
{
//setam tipul MIME pentru antetul HTTP
raspuns.setContentType(“text/html”);
PrintWriter iesire=raspuns.getWriter();
iesire.println(“<html><head>”);
iesire.println(“<title>Un exemplu de adunare</title>”);
iesire.println(“</head>”);
iesire.println(“<body><h1><b>Suma a doua numere double</b></h1>”);
iesire.println(“<p>Dati cele doua numere double</p>”);
iesire.println(“<form method=\”POST\” action=” + ”\”http://
localhost:8080/examples/servlet/Sumadouble\”>”);
iesire.println(“<p>Nr1: <input type=\”text\” name=\”nr1\” /></p>”);
iesire.println(“<p>Nr2: <input type=\”text\” name=\”nr2\” /></p>”);
iesire.println(“<input type=\”submit\” value=\”Trimite\” />”+
<input type=\”reset\” value=\”Sterge\” />”);
iesire.println(“</form></body></html>”);
iesire.close(); //inchidem fluxul de iesire
}
//procesam datele primite din formularul XHTML si returnam un
//document XHTML care afiseaza suma celor doua numere
protected void doPost(HttpServletRequest cerere, HttpServletResponse
raspuns)throws IOException
{
//setam tipul MIME pentru antetul HTTP
raspuns.setContentType(“text/html”);
String numar1=cerere.getParameter(“nr1”);
String numar2=cerere.getParameter(“nr2”);
double nr1=0.0,nr2=0.0;
Double d1Temp=Double.valueOf(numar1);
nr1=d1Temp.doubleValue();
Double d2Temp=Double.valueOf(numar2);
nr2=d2Temp.doubleValue();
double sumaDouble=nr1+nr2;
PrintWriter iesire=raspuns.getWriter();
iesire.println(“<html><head>”);
iesire.println(“<title>Un exemplu de adunare</title>”);
iesire.println(“</head>”);
iesire.println(“<body><h1><b>Suma celor doua double</b></h1>”);
iesire.println(“<p>Suma celor doua numere double este “ +
sumaDouble + “</p>”);
iesire.println(“</body></html>”);
iesire.close(); //inchidem fluxul de iesire
}
}//sfirsitul definitiei clasei SumaDouble
În exemplul de mai sus, metoda doGet() este folosită pentru a crea un formular simplu HTML. Cînd se apasă butonul Trimite, datele formularului sunt trimise către servlet și procesate de metoda doPost(). Metoda doPost() construiește un document HTML care conține suma celor două numere double trimise și apoi le returnează clientului. De altfel cînd se dorește citirea de informații de la client, se procedează în acest fel, adică folosind metoda doGet() se creează un formular HTML și apoi se prelucrează datele primite, returnîndu-se o pagină Web folosind metoda doPost().
Astfel dacă în formularul de mai sus am introduce nr1=10 și nr2=25, după apăsarea butonului Trimite, rezultatul ar fi următorul:
Servlet-uri și baze de date. JDBC – ODBC
Atît Java, cît și bazele de date sunt domenii importante ale informaticii. În acest capitol încercăm să arătăm cum am putea să combinăm Java și bazele de date.
JDBC (Java DataBase Connectivity) este o specificare API (Application Programming Interface), interfață standard SQL de acces la baze de date, dezvoltată de Sun Microsoft. Acesta ne furnizează un acces uniform la baze de date relaționale. JDBC este constituit dintr-un set de clase și interfețe scrise în Java, furnizînd un API standard pentru proiectanții de aplicații care accesează baze de date. Așadar este o parte a platformei Java și este inclusă în pachetul JDK (Java Development Kit). Acest lucru face posibilă scrierea aplicațiilor de baze de date folosind un API Java pur. Cea mai importantă funcție a JDBC-ului este posibilitatea lucrului cu instrucțiuni SQL (Structured Query Language) și procesarea rezultatelor într-o manieră independentă și consistentă. Folosind JDBC este ușor să transmitem secvențe SQL către baze de date relaționale. Cu alte cuvinte, nu este necesar să scriem un program pentru a accesa o bază de date Oracle, alt program pentru a accesa o bază de date Sybase și așa mai departe. Este de ajuns să scriem un singur program folosind API-ul JDBC și acesta va fi capabil să trimită secvențe SQL bazei de date dorite. Bineînțeles, scriind codul sursă în Java, ne este asigurată portabilitatea programului. Deci, iată două motive puternice care fac combinația Java – JDBC demnă de luat în seamă. Scrii un program o dată si acesta rulează peste tot (write once, run anywhere).
Fiind robust, sigur, ușor de folosit, ușor de înțeles, Java este un excelent limbaj pentru a dezvolta aplicații de baze de date. Tot ceea ce-i lipsește este modalitatea prin care aplicațiile Java pot comunica cu bazele de date. Aici vine însă JDBC-ul care oferă acest mecanism.
Ce face JDBC-ul? În linii mari JDBC face trei lucruri:
stabileste o conexiune cu o bază de date;
trimite secvențe SQL;
prelucrează rezultatele.
JDBC-ul este o interfață ,,low-level", adică este folosit pentru a executa direct comenzi SQL. El lucrează foarte bine în această postură și este mai ușor de folosit decît alte API-uri pentru conexiunea bazelor de date, dar totodată a fost proiectat astfel încît să fie baza pe care sa poți construi interfețe și unelte de nivel mai ridicat. Pîna la ora actuală există doua tipuri mai importante de API-uri JDBC:
Un preprocesor SQL încapsulat pentru Java. SGBD-urile relaționale implementează SQL, un limbaj specific folosit cu bazele de date. JDBC necesită ca secvențele SQL să fie trimise metodelor Java sub formă de șiruri de caractere. Un preprocesor SQL încapsulat permite folosirea secvențelor mixte SQL direct din Java. De exemplu, o variabila Java poate fi folosita într-o secvență SQL pentru a primi sau furniza valori SQL. Preprocesorul Java încapsulat transformă apoi aceasta mixtuă Java/SQL în apeluri JDBC.
O mapare directă a tabelelor bazei de date relaționale în clase Java. În această mapare, fiecare articol al tabelei devine o instanță a unei clase și fiecare cîmp corespunde unui atribut al instanței respective. În acest fel se poate opera direct cu obiecte Java.
Un driver JDBC traduce apelurile JDBC standard într-un protocol de rețea sau de bază de date sau într-un apel al bibliotecii bazelor de date care facilitează comunicarea cu baza de date. Acest nivel de traducere furnizează aplicații JDBC independente. Daca se schimbă "back-end"-ul bazei de date, atunci va fi modificat doar driverul JDBC cu modificările de cod necesare.
Există patru tipuri de drivere JDBC:
Puntea JDBC-ODBC acționează ca o legatură dintre JDBC și alt mecanism de conectivitate a bazelor de date numit ODBC (Object DataBase Connectivity). Acest driver este reprezentat în Java 2 SDK de clasa sun.jdbc.odbc.JdbcOdbcDriver și este folositor în aplicații de accesare a datelor unde nu există drivere JDBC pure. Puntea traduce metodele JDBC în apeluri de funcții ODBC. Puntea JDBC-ODBC necesită ca bibliotecile ODBC native și driverele să fie instalate și configurate pentru fiecare client ce folosește un driver de tip 1. Această cerință reprezintă o limitare serioasa pentru multe aplicații, funcționînd doar pentru sistemele de operare Microsoft Windows si Sun Solaris.
JAVA-API nativ. Aceste drivere folosesc interfața nativă JAVA (JNI – JAVA Native Interface) pentru a face apeluri la biblioteca unei baze de date locale API. De obicei, driverele de tip 2 sunt mai rapide decît cele de tip 1. Ca și driverele de tip 1, driverele de tip 2 necesită instalarea și configurarea bibliotecilor client bază de date native pe mașina client. Sunt foarte convenabile cînd există biblioteci de acces al datelor scrise in limbajul C, însă acestea nu sunt foarte portabile pe toate platformele.
JAVA-protocol de rețea. Aceste tipuri de drivere sunt drivere JAVA pure care folosesc un protocol de rețea (de obicei, TCP/IP) pentru a comunica cu aplicația JDBC "middleware". Apoi, aplicatia JDBC middleware traduce cererile JDBC folosind protocolul de rețea în apeluri de funcții specifice bazelor de date. Tipul 3 de drivere reprezintă soluția cea mai flexibilă deoarece nu necesită biblioteci de bază de date native pe client și se poate conecta la mai multe baze de date diferite.
JAVA-protocol bază de data. Aceste tipuri de drivere sunt drivere JAVA pure care implementează un protocol de bază de date (cum ar fi Oracle SQL Net) pentru a comunica direct cu baza de date. De aceea, aceste drivere sunt cele mai rapide. Ca și driverele de tip 3, ele nu necesită biblioteci de bază de date native și pot fi folosite pe Internet fără instalarea clientului. Driverele de tip 4 sunt specifice bazelor de date. Spre deosebire de driverele de tip 3, dacă se schimba tipul bazei de date de la server, atunci trebuie instalat un nou driver pentru acel tip de bază de date.
Cea mai buna sursă de informare a listei de drivere JDBC disponibile este vizitînd site-ul firmei Sun. De exemplu la adresele URL: http://www.javasoft.com/products/jdbc/,
http://industry.www.java.com/products/jdbc/drivers
În general, driverele de tip 1 si 2 sunt gratuite, însă necesită instalarea la client a bibliotecilor ODBC native. Driverele de tip 3 sunt în general comerciale și se pot folosi in aplicații flexibile pe mai multe platforme. Driverele de tip 4 sunt mai rapide, unele sunt chiar gratis, însă se pot folosi doar pentru aplicații particulare.
Procesul obținerii de informații dintr-o bază de date folosind JDBC
implică în principiu cinci pasi:
înregistrarea driverului JDBC cu managerul de drivere;
stabilirea unei conexiuni către baza de date;
execuția unei instrucțiuni SQL ;
procesarea rezultatelor;
închiderea conexiunii cu baza de date.
Înregistrarea driverului JDBC cu managerul de drivere. JDBC API folosește un manager de drivere și drivere specifice bazelor de date pentru furnizarea de conexiuni transparente către baze de date eterogene. Managerul de drivere JDBC asigură că este folosit driverul corect pentru accesarea fiecărei date sursă. Managerul de drivere este capabil să suporte drivere concurente multiple conectate la baze de date eterogene multiple. Treaba managerului de drivere este să mențină o referință către toate obiectele driver disponibile pentru clienții JDBC. Un driver JDBC se inregistrează automat cu managerul de drivere atunci cînd este încărcat. În fapt, un driver JDBC este o clasă JAVA care stă în CLASSPATH. Pentru încărcarea unui driver JDBC, folosim metodele
Class.forName() și newInstance():
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver").newInstance();
În acest exemplu, am incărcat driverul punte JDBC-ODBC din pachetul sun.jdbc.odbc.
Class.forName() este o metodă statică care instruiește mașina virtuală JAVA să aloce dinamic, să încarce și să facă o legatură la clasa specificată. Dacă acea clasă nu este gasită, atunci se
aruncă o excepție ClassNotFoundException. Metoda newInstance() indică faptul că trebuie creată o nouă instanță a clasei respective.
Driverele pot fi înregistrate și folosind metoda DriverManager.registerDriver().
Stabilirea unei conexiuni catre baza de date. Odată ce s-a încărcat un driver, putem să-l folosim pentru stabilirea unei conexiuni către baza de date. O conexiune JDBC este identificată prin URL-ul bazei de date care precizează driverul și datele sursa. Sintaxa standard pentru URL-ul unei baze de date este: jdbc:subprotocol:nume. Prima parte precizează că pentru stabilirea conexiunii se folosește JDBC. Partea de mijloc (subprotocol) este un nume de driver valid sau a altei soluții de conexiune a bazelor de date. Ultima parte este un nume logic sau alias care corespunde bazei de date fizice.
Cele mai cunoscute drivere JDBC au sintaxa:
ODBC – jdbc:odbc:numeSursaDate
MySQL – jdbc:mysql://server[:port]/numeBazaDate
Oracle – jdbc:oracle:thin:@server:port:numeInstanta
Pentru stabilirea unei conexiuni la o bază de date, se folosește metoda statică getConnection() din clasa DriverManager. De exemplu:
Connection conexiuneBazaDate=DriverManager.getConnection("jdbc:mysql:
//localhost/studenti");
Pentru baze de date care necesită autentificare, atunci utilizăm o forma a acestei metode cu 3 argumente: Connection conexiuneBazaDate = DriverManager.getConnection(
"jdbc:mysql://localhost/studenti", numeUtilizator, parola);
După ce s-a stabilit conexiunea se pot trimite instrucțiuni SQL către baza de date. O instrucțiune poate face anumite operații pe baza de date, cum ar fi căutare, inserare, actualizare sau ștergere. Pentru execuția unei instrucțiuni SQL, se folosește metoda createStatement() aplicată unui obiect Connection. Această metodă întoarce un obiect din clasa Statement.
Statement instructiune = conexiuneBazaDate.createStatement();
Putem aplica metoda executeQuery() obiectului de tip Statement pentru a selecta anumite înregistrări din baza de date. Rezultatul întors va fi un obiect din clasa ResultSet care apoi poate fi inspectat. Pentru operațiile de inserare, actualizare sau ștergere se folosește metoda executeUpdate() aplicată obiectului de tip Statement.
ResultSet rs = instructiune.executeQuery("select * from studenti");
String sql = "insert into studenti values (1, "Pop", "Dan", 9)";
ResultSet rs = instructiune.executeUpdate(sql);
Procesarea rezultatelor. Pentru parcurgerea înregistrărilor unui obiect din clasa ResultSet,putem folosi metodele next() și previous().
Continuînd exemplul de mai sus, avem:
while (rs.next())
{
System.out.println(rs.getString("nr") + ", " +
rs.getString("nume") + ", " +
rs.getString("prenume") + ", " +
rs.getString("nota"));
}
Închiderea unei conexiuni la o bază de date. După ce procesarea datelor s-a încheiat, conexiunile către baza de date trebuie închise pentru că sunt o resursă importantă și limitată. Pentru aceasta se folosește metoda close() aplicată obiectului Connection. În plus, trebuie închise (înaintea acesteia) și obiectele Statement și ResultSet folosind metodele lor (care se cheamă tot close()). "Colectorul de gunoaie" le va elibera zona de memorie ocupată.
try
{ . . .
rs.close();
instructiune.close();
}
catch (SQLException e)
{
System.out.println("Eroare cautare date din: " + URLBazaDate);
}
finally
{
try
{
if (conexiuneBazaDate != null)
{
conexiuneBazaDate.close();
}
}
catch (SQLException ignored) {}
}
SQLJ este o cale standard de includere a comenzilor SQL statice direct în programe Java. Noua tehnologie este rezultatul unei colaborări Oracle, IBM, Sybase, Tandem și Informix, după modelul standardului ANSI/ISO de legare a comenzilor SQL în programe C, COBOL, FORTRAN sau alte limbaje. Oracle implementase anterior Pro*C un produs ce permite utilizarea comenzilor SQL în programe C. Cu alte cuvinte, SQLJ ar fi un soi de Pro*Java. Deoarece Pro*anything desemnează o tehnologie (specifică)/ proprietară Oracle, iar SQLJ este un standard deschis, nu s-a ales denumirea de Pro*Java. Deși SQLJ simplifică scrierea, gestiunea și depanarea aplicațiilor Java, el nu înlocuiește JDBC. Din contră, implementarea Oracle pentru SQLJ chiar folosește JDBC. Pentru a înțelege cum funcționează SQLJ să luăm următorul exemplu: presupunem că avem o tabelă, emp, în baza de date care conține informații despre angajații unei firme, iar printre câmpurile acesteia: ename – numele și sal – salariul angajatului. Dorim să afișăm numele tuturor angajaților care au salariul mai mare decât o valoare dată. Codul JDBC în acest caz ar putea fi:
// (Presupunem că avem deja un obiect JDBC Connection conn)
// definim variabile Java
String name;
int id=37115;
float salary=20000;
// Construim un obiect JDBC PreparedStatement.
PreparedStatement pstmt = conn.prepareStatement
("select ename from emp where empno=? and sal>?");
pstmt.setInt(1, id);
pstmt.setFloat(2, salary);
// Executam o interogare; valorile obtinute sunt
//asignate unei variabile Java.
ResultSet rs = pstmt.executeQuery();
while (rs.next()) {
name=rs.getString(1);
System.out.println("Numele este: " + name);
}
rs.close()
pstmt.close();
Primele trei linii definesc variabilele Java name, id și salary. Următoarea linie realizează un apel de pregătire a unei instrucțiuni (prepared statement). Presupunând că deja este stabilită o conexiune prin JDBC la baza de date, putem folosi metoda prepareStatement() – metodă a obiectului conn derivat din clasa Connection (conexiune). O PreparedStatement este folosită atunci cînd în interiorul comenzii SQL avem nevoie de un set dinamic de valori, ceea ce înseamnă că o aceeași instrucțiune gata pregătită (prepared statement) poate fi folosită de oricîte ori pentru diferite valori ale variabilelor. Interogarea este formată într-o instanță (pstmt) a clasei PreparedStatement în timp valorile variabile sunt păstrate în variabile Java și transmise prin apelurile pstmt.setInt() și pstmt.setFloat(). Primul "?"este înlocuit cu valoare variabilei id, deci un int a cărui valoare este 37115. Al doilea "?" este înlocuit cu valoarea variabilei salary (un float a cărui valoare este 20000). Urmează execuția interogării, datele rezultat fiind returnate într-un obiect ResultSet al JDBC. În final, datele sunt extrase din setul rezultat și afișate. Un set rezultat conține de obicei mai multe linii de date, dar în acest exemplu avem o singură linie. După cum se poate lesne constata, avem destul de lucru pentru a realiza o simplă interogare. Ne putem imagina atunci complexitatea apelurilor SQL multilinie în care avem mai multe variabile Java. Inevitabil după un timp ne vom pune întrebarea "Cum putem face ca din Java interogările să fie mai ușor de realizat?" . Așa cum bănuiți deja răspunsul la întrebarea noastră este: folosind SQLJ. Pentru a ne da seama de avantajele SQLJ să transcriem exemplul de mai sus folosind SQLJ:
String name;
int id=37115;
float salary=20000;
#sql {select ename into :name from emp where empno=:id and sal>:salary);
System.out.println("Name is: " + name);
Ce observăm analizând acest cod? Primul lucru, un cod mult mai concis! Explicația? SQLJ permite comenzilor SQL să fie legate direct în codul Java și acceptă ca variabilele Java să fie folosite direct în comenzile SQL. Mai observăm că fiecare variabila Java este precedată de două puncte ":" și că prin intermediul acestor variabile Java putem extrage cu ușurință rezultatul interogării, dacă … numai dacă rezultatul execuției comenzii SQL este o reprezentat de o singură linie (o înregistrare)!
În listingul alăturat este sursa unui servlet care accesează o bază de date relațională. Servletul este un contor care numară de câte ori este accesată o pagina Web. Se folosește interfața JDBC pentru a accesa o bază de date MiniSQL în care sunt stocate datele. În baza de date avem un tabel tabel_contor care conține doua cîmpuri: pagina_WWW si nr_accesari. Servletul este accesat prin intermediul tag-ului SERVLET din fișiere .shtml și are un singur parametru: URL-ul paginii respective. Servletul are trei metode mai importante. La prima cerere, cînd servletul este încărcat, serverul apelează metoda init. Această metodă accesează resursele necesare prelucrării cererilor, adică se creează o conexiune la baza de date. Ca să poată să facă acest lucru, se impune crearea unei referințe la un driver de baze de date folosit la comunicația cu baza de date mySQL. Metoda care este apelată la fiecare cerere, service, se folosește de conexiunea la baza de date pentru a accesa datele prin intermediul cererilor SQL. Această metodă preia un singur parametru, numele paginii Web, din obiectul ServletRequest, cu ajutorul metodei getParameter. În continuare, se trimite o cerere SELECT către baza de date, pentru a se caută linia din tabelul tabel_contor care conține datele pentru pagina respectivă. Daca nu s-a găsit linia respectivă înseamnă că pagina este accesată prima dată, și inserăm în tabel noul URL și valoarea 1. Dacă o linie a fost găsită, se face o incrementare și apoi un UPDATE în baza de date. La sfîrsit se scrie rezultatul în stream-ul de ieșire, pe care îl accesăm prin intermediul obiectului ServletResponse. Metoda destroy închide conexiunea la baza de date. În toate cele trei metode se folosește metoda log, care scrie în fișierul jurnal modul cum s-au terminat operațiile, pentru o eventuală analiză ulterioară. Metoda getServletInfo este echivalentul metodei getAppletInfo din apleturi și returnează informații despre servlet. Exemplul prezentat demonstrează ușurința cu care putem accesa o bază de date. Bineînțeles, se pot crea aplicații care accesează o bază de date ce conține structuri de date mai complexe, care pot fi prezentate utilizatorului în pagini Web avînd o formă intuitiva. Este posibil chiar ca utilizatorul să poată crea cereri către o baza de date.
Aplicatie servlet
import java.servlet.*;
public class CounterServlet extends GenericServlet {
static java.sql.Connection conexiunea;
public void init() throws ServletException {
String driver, dataBase, user, pass;
log("init");
if( (driver = getInitParameter("driver")) == null )
driver = "imaginary.sql.iMsqlDriver";
if( (dataBase = getInitParameter("db")) == null )
dataBase = "jdbc:msql://localhost/test_db";
if( (user = getInitParameter("user"))== null )
user = "vlad";
if( (pass = getInitParameter("password"))== null )
pass = "";
try {
Class.forName(driver);
conexiunea = java.sql.DriverManager.getConnection( dataBase,user,pass);
log(" Connection to "+dataBase+" created.");
}
catch( java.sql.SQLException e ) {
e.printStackTrace();
log("Failed to connect to "+dataBase+".");
throw new ServletException("Can't open database");
}
catch( ClassNotFoundException e ) {
e.printStackTrace();
log("Unable to load database driver: "+driver+".");
}
}
public void destroy() {
log("destroy");
try {
conexiunea.close();
log("Connection to database closed.");
} catch( java.sql.SQLException e ) {
e.printStackTrace();
log("Failed to close database.");
}
}
public String getServletInfo() {
return "Page Counter Servlet";
}
Aplicatie serviciu
public void service( ServletRequest req, ServletResponse res )
throws ServletException, java.io.IOException {
int contor = 0;
String page;
if( (page = req.getParameter("page")) == null ) {
log("Wrong servlet call, parameter \"page\" not present.");
throw new ServletException("Wrong servlet call");
}
try {
java.sql.Statement statement = conexiunea.createStatement();
java.sql.ResultSet result_set;
String sqlQuery = null;
result_set = statement.executeQuery("SELECT nr_accesari FROM tabel_contor " +
"WHERE pagina_WWW = '" + page + "'");
if( result_set.next() )
contor = result_set.getInt(1);
else contor = 0;
statement.close();
statement = conexiunea.createStatement();
if( ++contor == 1 ) {
sqlQuery = "INSERT INTO tabel_contor " +
"(pagina_WWW, nr_accesari) " +
"VALUES ('" + page + "', " +contor+ ")";
}
else {
sqlQuery = "UPDATE tabel_contor " +
"SET nr_accesari = " + contor + " " +
"WHERE pagina_WWW= '" + page + "'";
}
statement.executeUpdate(sqlQuery);
statement.close();
}
catch( java.sql.SQLException e ) {
e.printStackTrace();
}
java.io.PrintStream out = new java.io.PrintStream( res.getOutputStream() );
out.println(contor);
out.close();
}
}
Prezentăm mai jos o aplicație Java (aplicația nu este un servlet ci o aplicație de sine-stătătoare) care va accesa o bază de date, oferind utilizatorului posibilitatea de a lista înregistrările bazei de date și de a adăuga înregistrări în baza de date.
Am construit mai întîi baza de date (folosind Microsoft Visual FoxPro) cu următoarea structură
Astfel în acest moment în directorul D:\VLAD\Cursuri_postuniv\Licenta\Baza date avem o bază de date Baza.dbc ce conține un tabel Catalog1.dbf. Acum trebuie încărcat driverul ODBC, necesar pentru obținerea unei conexiuni cu baza de date creată:
Apăsînd butonul Add alegem din lista derulantă opțiunea corespunzătoare.
Pasul următor este compilarea fișierului TestJDBC.java de mai jos și, în cazul în care compilarea nu a semnalat erori, lansarea în execuție a aplicației (se crează un proiect la care se atașează fișierul TestJDBC și apoi se rulează proiectul) eventual de la linia de comandă folosind comanda C:\>jdk1.3.1\bin\java TestJDBC
import java.sql.*;
import java.io.*;
import java.lang.*;
public class TestJDBC
{
public static void main (String[] args)
{
String dbUrl = "jdbc:odbc:baza";
String user = "dbf";
String password = "sql";
String nu="",nu1="", prenu="", prenu1="", cla1="", cla="";
int comanda=0;
System.out.println("1.Afisati ");
System.out.println("2.Adaugati ");
BufferedReader stdin= new BufferedReader(new
InputStreamReader(System.in));
try{
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
}
catch(ClassNotFoundException e)
{
e.printStackTrace();
System.out.println("Eroare incarcare driver!\n" + e);
}
while (1<2)
{
System.out.println("\ncomanda > ");
try{
comanda=Integer.parseInt(stdin.readLine());
}catch (IOException f){}
switch (comanda)
{
case 1:
try{Connection c=DriverManager.getConnection
(dbUrl, user, password);
Statement s= c.createStatement();
ResultSet r =s.executeQuery(
" SELECT prenume, nume, clasa FROM catalog1"+
" ORDER BY nume");
while (r.next())
{
System.out.println (r.getString ("nume").trim() + "|" + r.getString ("prenume").trim()+"|"+r.getString ("clasa") );
}
s.close();
} catch(SQLException e) {e.printStackTrace();}
break;
case 2:
try{Connection c=DriverManager.getConnection(
dbUrl, user, password);
Statement s= c.createStatement();
try{
System.out.print("Introduceti numele: ");
nu=stdin.readLine();nu1="\""+nu+"\"";
System.out.print("\nIntroduceti prenumele: ");
prenu=stdin.readLine();prenu1="\""+prenu+"\"";
System.out.print("\nIntroduceti clasa: ");
cla=stdin.readLine();cla1="\""+cla+"\"";
}catch (IOException g){}
s.executeUpdate(" INSERT INTO catalog1 (nume, prenume, clasa) VALUES ("+nu1+" , "+prenu1+" , "+cla1+")");
} catch(SQLException e) {e.printStackTrace();}
break;
default: System.out.println("Optiunea aleasa nu exista");
}//end switch
}//end while
}
}
Efectul este următorul:
1.Afisati
2.Adaugati
comanda >
1
||
Agachi|Andrei|a VIIIa A
Agafitei|Dan Adrian|a VIIIa B
Chicet|Georgeta|a VIIIa A
Popa|Ion|a X a
comanda >
2
Introduceti numele: Onutu
Introduceti prenumele: Bogdan
Introduceti clasa: a VII a C
comanda >
1
||
Agachi|Andrei|a VIIIa A
Agafitei|Dan Adrian|a VIIIa B
Chicet|Georgeta|a VIIIa A
Onutu|Bogdan|a VII a C
Popa|Ion|a X a
comanda >
Prezentarea aplicației
Voi prezenta mai întîi structura site-ului Web (inclusiv codul HTML).
Pagina de start (front-page)
Codul HTML pentru pagina de start este dat mai jos (VladS1.html).
<html>
<style>
header {
position: absolute;
top: 0px;
left: 0px;
right: 0px;
color: blue;
background: rgb(200,200,220);
}
body { color:red; background:url("Soap Bubbles.bmp");
margin:0.1cm;}
h1 {text-align:center;color:white;background:url("imag04.gif");}
h2 {border:solid;
border-width:2pt;
border-color:yellow;
background:url("imag01.gif");}
address {text-align:center; color:navy ;background:url("imag051.jpg");}
a:hover {color:green;background:white;}
img {margin:0.2cm;}
</style>
<head><title>Şcoala Bălţaţi</title></head>
<frameset rows="150,*" border=1 scrolling=no>
<frame src="titlu.html" marginheight=2>
<frameset cols="220,*">
<frame src="stinga.html" noresize>
<frame src="VladS2.html">
</frameset>
</frameset>
<hr size=5 width="80%">
<p><address><b>Şcoala coordonatoare Bălţaţi cu clasele I-VIII /
com.Bălţaţi, jud.Iaşi, E.583 km.35, Tel.032/717350,
E-mail: <a href ="http://webmail.dntis.ro"
/*onMouseOver="window.status='';return true;"*/>
[anonimizat] </a><b></address></p>
<h5 align="center"><font face="Arial Helvetica, sans-serif" size="1">
<a href="http://www.yahoo. com" /*onMouseOver="window.status='';
return true;"*/>Trimite mesaje</a> |
<a href="#t">Top of page</a> |
<font color="#FF0000"><u><a href="VladS1link.html"> Prezentarea
şcolii </a></u></font> |
<a href="VladS1link.html">Organigrama </a> | <b><a href="VladS1link.html"> Educaţia 2000+ </a> | <a href="http://webmail.dntis.ro"
onMouseOver="window.status='Citeste mesaje'; return true;">
Posta electronica </a></h5>
</body>
</html>
Pagina este împărțită, după cum se observă în frame-uri, aplicația pentru accesarea arhivei – baza de date ce conține dosarele elevilor – găsindu-se în frame-ul din partea stîngă. În acest cadru se încarcă fișierul stinga.html
<html>
<head><style>
body { color:white; background:url("Soap Bubbles.bmp");
margin:0.1cm;}
p {text-align:center; font size=1; face=Times New Roman;}
img {margin:0.2cm;}
</style><head>
<body>
<center>
<img src="mascota.gif" width="90%" height="30%" align=center></center>
<p><center><b>Arhiva<br>Completati formularul pentru cautare<b></center>
</p>
<form method="POST" action=
"http://localhost:8080/examples/servlet/Arhiva">
<center>Numele:<br>(e.g: Popescu)<br><input type="text"
name="nume" size="20"><br>
Prenumele:<br>(e.g: Marian, doar primul prenume)<br><input type ="text" name="prenume" size="20"><br>
Anul absolvirii:<select name="an_abs"><option selected>
(Alege anul absolvirii)
<option>1995
<option>1996
<option>1997
<option>1998
<option>1999
<option>2000
<option>2001</select><br><br>
<input type="submit" value="Trimite formularul"><br><br>
<input type="reset" value="Stergeti"><br>
</form>
</body>
</html>
Fișierul titlu.html
<html>
<style>
body { color:white; background:url("Soap Bubbles.bmp");
margin:1cm;}
h1 {text-align:center;color:white;background:url("imag04.gif");}
background:url("imag01.gif");}
</style>
<body onload="alert('S-a incarcat pagina!');"
"window.defaultStatus='Welcome!';">
<a name="t"></a>
<div class="header"><marquee><font size="14">Bine ati venit pe site-ul Scolii Baltati</b></font>.</marquee></div>
<h1 onDblClick="alert('lucruri interesante despre o şcoală ');">
<font face="Times New Roman" size="5">Şcoala coordonatoare cu clasele I-VIII Bălţaţi</font></h1>
<div id="afis" style="position: absolute;">
Salutare…</div>
<script language="JavaScript">
afis.style.pixelLeft = 30;
afis.style.pixelTop = 8;
afis.style.pixelWidth = 200;
afis.style.pixelHeight = 40;
afisare = new Array();
afisare[0] = "Bine ati venit…";
afisare[1] = "…pe site-ul şcolii Bălţaţi… ";
afisare[2] = "Cea mai tare şcoală din mediul rural";
i = 0;
function go_on()
{
afis.innerHTML = afisare[i];
i++;
if (i==3)
{
i=0;
setTimeout("go_on()",1800);
}
else
setTimeout("go_on()",600);
}
setTimeout("go_on()",1000);
</script>
</body></html>
În frame-ul de pe al doilea rînd, coloana din dreapta se încarcă fișierul VladS2.html. În cadrul paginii VladS2.html există o bară cu mai multe link-uri dintre care unele fac trimitere către prezentarea unor programe și activități educative derulate în Școala Bălțați, informații care se găsesc în VladS1link.html
Fișierul VladS1link.html:
<html>
<head>
<style>
body { color:red; background:url("fondlink.jpg");
margin:1cm;}
h1 {text-align:center;background:url("tabellink.jpg");}
</style>
</head>
<body>
<div class="header"><marquee><b><font size="14">
O scurtă prezentare a şcolii noastre …</b></font></marquee>
</div>
<div id="afis" style="position: absolute;">
Salutare…
</div>
<script language="JavaScript">
afis.style.pixelLeft = 30;
afis.style.pixelTop = 8;
afis.style.pixelWidth = 350;
afis.style.pixelHeight = 40;
afisare = new Array();
afisare[0] = "Bine ati venit…";
afisare[1] = "…pe site-ul şcolii Bălţaţi… ";
afisare[2] = "Cea mai tare şcoală din mediul rural";
i = 0;
function go_on()
{
afis.innerHTML = afisare[i];
i++;
if (i==3)
{
i=0;
setTimeout("go_on()",1800);
}
else
setTimeout("go_on()",600);
}
setTimeout("go_on()",1000);
</script><center>
<a href="VladS2.html"> Home </a> |
<a href="#t"> Sfârşit de pagina </a>
<center><img src="imag091.jpg" height="25%" width="18%" align=center
vspace=10></center>
Ne straduim sa dam un sens vietii lor
<hr size=6 width="100%">
<h1><table align="center" cellpading="7" >
<tr><td colspan=2> 1.Prezentarea şcolii </td>
<td><dl><dt><a name="Prezentarea şcolii">
<dd> …Şcoala BĂLŢAŢI Îşi propune să ……
……
</dl></td></tr>
<tr><td colspan=2> 2.Organigrama </td>
<td><dl><dt><a name="Organigrama">
<dd>În anul 2001-2002 vor fi amenajate:<br>
<12 săli de clasă<br>
<1 laborator de fizică<br>
……
</dl></tr></h1>
<tr><td colspan=2> 3.Educaţa 2000+ </td>
<td><dl><dt><a name="Proiectul Educaţia 2000+">
<dd>Proiectul "EDUCAŢIA 2000+" , care se derulează în şcoala BĂLŢAŢI din septembrie 1999 ……
……
</dl></td></tr></table>
<a name="t"></a>
</body>
</html>
După ce site-ul Web a fost conceput, urmează scrierea servlet-ului pentru procesarea datelor din formular și încărcarea serverului Web Apache-Tomcat.
Servlet-ul
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
import java.util.*;
import java.sql.*;
/**
* <p>Title: Arhiva</p>
* <p>Description: Procesare formular – site scoala</p>
* <p>Copyright: Copyright (c) 2002</p>
* <p>Company: Scoala Baltati</p>
* @author Sacalus Vlad
* @version 2.0
*/
public class Arhiva extends HttpServlet
{
private static final String CONTENT_TYPE = "text/html; charset=windows-1250";
public void init() throws ServletException {}
//Process the HTTP Post request
protected void doPost(HttpServletRequest cerere, HttpServletResponse raspuns) throws ServletException, IOException
{
raspuns.setContentType(CONTENT_TYPE);
String dbUrl = "jdbc:odbc:Arhiva";
try
{
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver").newInstance();/*Incarcarea driverului si stabilirea conexiunii cu baza de date*/
} catch(Exception e) {e.printStackTrace();
System.out.println("Eroare incarcare driver!\n" + e);}
try
{
Connection conexiuneBazaDate=DriverManager.getConnection(dbUrl);
Statement instructiune=conexiuneBazaDate.createStatement();
PrintWriter out = raspuns.getWriter();
String numele=(cerere.getParameter("nume")).trim();
String prenumele=(cerere.getParameter("prenume")).trim();
String sql="SELECT * FROM Tabel_arhiva WHERE Tabel_arhiva.Nume=\""+
numele+"\" AND Tabel_arhiva.Prenume=\""+prenumele+"\"";
ResultSet rs=instructiune.executeQuery(sql);//Interogarea bazei de date
out.println("<html>");/*Generarea documentului HTML raspuns continind datele rezultate din interogare*/
out.println("<head><title>Arhiva</title></head>");
out.println("<body>");
out.println("<p>Rezultatele dupa cautarea in baza de date sunt urmatoarele: </p>");
out.println("<ul>");
while (rs.next())
{
out.println("<li><b>Nume, prenume:</b><br>"+ rs.getString ("Nume").trim()+" "
+ rs.getString ("Prenume").trim() + "</li><br>");
out.println("<li><b>Diriginte:</b><br>"+ "Prof.
"+rs.getString ("Prof_diriginte").trim() + "</li>");
out.println("<li><b>Clasa:</b><br>"+ rs.getString ("Clasa").trim() + "</li><br>");
out.println("<li><b>Media absolvire:</b><br>"+ rs.getString ("Media").trim()
+ "</li><br>");
out.println("<li><b>Data nastere:</b><br>"+ rs.getString ("Data_nast").trim()
+ "</li><br>");
out.println("<li><b>Locul nasterii:</b><br>"+ rs.getString ("Locul_nast").trim()
+ "</li><br>");
out.println("<li><b>Domiciliul:</b><br>"+ rs.getString ("Domiciliu").trim()
+ "</li><br>");
out.println("<li><b>CNP:</b><br>"+ rs.getString ("Cnp").trim() + "</li><br>");
out.println("<li><b>Seria BI:</b><br>"+ rs.getString ("Seria_bi").trim()
+ "</li><br>");
out.println("<b>Foto:</b><br><img src=\""+"C"+":"+\\Tomcat\\jakarta
+"-"+"tomcat\\webapps\\Html\\"+numele+".jpg"+"\" height="+"\"25%\"+ width="+"\"80%\">
<br>");
out.println("<li><b>Informatii:</b><br>"+ rs.getString ("Info").trim()
+ "</li><br>");
}//end while
out.println("</ul>");
out.println("</body></html>");
}//end try
catch(SQLException e) {e.printStackTrace();}
}//end doPost
//Clean up resources
public void destroy() {}
}//end class
După compilare trebuie încărcat serverul Tomcat. Încărcăm toate fișierele HTML într-un director pe care îl punem în ierarhia de directoare a serverului C:\Tomcat\jakarta-tomcat\webapps\Html. Odată cu crearea acestui nou director în ierarhia de directoare a serverului trebuie precizată această modificare, adăugînd următoarea linie în fișierul C:\Tomcat\jakarta-tomcat\conf\server.xml
<Context path="/Html" docBase="webapps/Html" debug="0" reloadable="true" >
</Context>
Servlet-ul Arhiva.class trebuie pus în directorul
C:\Tomcat\jakarta-tomcat\webapps\examples\WEB-INF\classes
Acum trebuie încărcat driverul ODBC, necesar pentru obținerea unei conexiuni cu baza de date creată (vezi pag.23) folosind Data Source Name: Arhiva și Path: C:\Tomcat\jakarta-tomcat\webapps\BazaDate\Tabel_arhiva.dbf; în acest moment trebuie pornit serverul
(fișierul C:\Tomcat\jakarta-tomcat\bin\startup).
Folosind un browser Web și solicitînd URL-ul: http://localhost:8080/Html/VladS1.html, obținem ca efect încărcarea paginii de start a site-ului descris mai sus. Completînd formularul și apăsînd butonul Trimiteti obținem în frame-ul corespunzător fișierului stinga.html dosarul elevului al cărui nume a fost introdus în cîmpurile formularului.
Rezultatele dupa cautarea in baza de date sunt urmatoarele:
Nume, prenume:
Hariga Bogdan
Diriginte:
Prof. Sacalus Vlad
Clasa:
a VIII-a A
Media absolvire:
8.56
Data nastere:
1987-03-16
Locul nasterii:
Tg. Frumos
Domiciliul:
Sat Baltati, com. Baltati, jud. Iasi, E. 583 km. 35, Tel. 032-717130, Fam. Hariga
CNP:
1870316221186
Seria BI:
MX 032665
Foto:
Informatii:
Foarte ambitios, dar pornirile sale- in domeniul didactic nu dureaza mult. Dupa un timp scurt renunta la orice initiativa sau plan. Necesita indrumare si supraveghere. Coeficient IQ 120. Capabil de efort intelectual indelungat. Are tendinta de a ,,poza" in ,,bufonul" clasei. Sociabil. Nu are execese de personalitate si nu ridica probleme prin comportament.
Anexe
Anexa1. Arhitectura client-server
Cel mai simplu exemplu care să ilustreze ce este arhitectura client-server este următorul: un utilizator lansează un program specializat de pe calculatorul în fața căruia se află, prin intermediul căruia dorește să se conecteze la un alt calculator aflat la distanță. În mod evident pe calculatorul aflat la distanță va trebui să existe un program specializat, complementar care va accepta sau nu cererea de conectare. Din moment ce conexiunea a fost stabilită utilizatorul poate comunica cu calculatorul aflat la distanță (cere și transferă date, trimite comenzi, etc.). Programul sau procesul care inițiază o astfel de comunicare se numește client, iar prin extensie și calculatorul pe care rulează programul client se va numi tot client. Programul satisface cererea emisă de client se numește server, iar prin extensie calculatorul pe care rulează se numește tot server. Se observă că totdeauna clientul este cel care inițiază comunicarea, programul server nefăcând altceva decât să aștepte cereri de conectare și eventual să le satisfacă.
În cadrul arhitecturii client-server două sau mai multe procese, eventual rulând pe calculatoare diferite, cooperează în realizarea unui anumit task.
Un calculator server trebuie să poată deservi simultan mai multe cereri de conectare, astfel încât cerințele hardware impuse unui server sunt considerabile. Din acest motiv programele server rulează pe calculatoare de mare putere, specializate, diferite chiar și prin configurația hardware de calculatoarele client, care pot avea performanțe mai slabe, întrucât nu necesită alocarea de resurse prea mari pentru a efectua cereri.
Pentru ca două calculatoare să poată comunica între ele după modelul distribuit client-server nu este necesar ca pe cele două mașini să ruleze același sistem de operare și nici măcar să fie compatibile hardware ci, este suficient ca programele de comunicație (clientul și serverul) să respecte protocoalele standard de comunicare în rețea.
Modelul distribuit sau arhitectura client-server este modelul pe care s-a fundamentat dezvoltarea conceptului de rețea de calculatoare, stând la baza funcționalității Internetului și a serviciilor oferite de acesta: poștă electronică, web, etc.
Fig.5 Arhitectura client – server
Există mai multe tipuri de servere și anume:
-servere concurente care pot rezolva mai multe cereri simultan;
-servere iterative care pot rezolva o singură cerere la un moment dat.
Anexa 2.Formulare HTML
Un formular HTML se definește folosind tag-uri specifice pentru afișarea conșinutului și introducerea datelor de către client. Acestea sunt un subset HTML care permite achiziționarea de informații prin intermediul unor figuri grafice de genul butoanelor radio (radio button), căsuțelor de selecție (check-box), cîmpurilor text (text-area) sau a listelor . După ce utilizatorul a terminat de completat un asemena formular, îl expediază, prin intermediul unui buton Submit, programului CGI care prelucrează informația și returnează un răspuns pe baza unor criterii de selecție impuse de utilizator.
În cadrul marcajului <form> se pot include si alte tag-uri uzuale HTML, plus diverse tag-uri indicind modalitati de introducere a datelor in cadrul formularului. În cea mai mare parte,toate câmpurile unui formular,destinate introducerii datelor,se specifica prin intermediul elementului <input> utilizindu-se diferite atribute. Atributele uzuale ale unui element <input> sint urmatoarele:
•type= opt indica tipul cimpului de introducere a datelor (vezi mai jos);
•name= nume furnizeaza numele cimpului,acest nume fiind folosit de catre programul de prelucrare a formularului;
•value= valoare contine valoarea implica a cimpului de intrare;
•checked= validare indica pentru un buton de selectie daca acea optiune este selectata implicit sau nu;
•size= numar stabileste latimea de afisare a unui cimp de date (in mod uzual,un cimp text);
•maxlenght= numar determina lungimea maxima a valorilor introduse de utilizatorilor intr-un cimp de tip text.
Daca se doreste introducerea unor informatii text pe mai multe linii,se poate utiliza elementul <textarea>Pentru casete de validare (avind butoane sau optiuni multiple)se foloseste elementul <select>
a.TEXT este tipul prestabilit,cu SIZE utilizat pentru a specifica marimea prestabilita a casetei care se creeaza.Urmatorul cod HTML va genera un astfel de dialog text (cimpul text este complet inutil,fiindca nu va fi prelucrat de nici un program):
<form>
<input type="text" size="30" value="Text initial" name="cimp1">
</form>
Top of Form
Bottom of Form
b.PASSWORD (parola)este un cimp de text,dar datele introduse de utilizator sint afisate prin caracterul * sau alte simboluri,din motive de securitate.Optiunea MAXLENGTH poate fi folosita sa specifice numarul maxim de caractere permis pentru parola.In mod uzual,valoarea cimpului va fi trimisa necriptata serverului Web.
<form>
<p>Dati parola sa intrati in rai:
<input type="password" size="20" name="pass" maxlenght="25">
</form>
Dati parola sa intrati in rai:
c.CHECKBOX (buton de validare)asigura o singura caseta (negrupata)de validare;atributul CHECKED permite sa specificam daca aceasta caseta urmeaza sau nu sa fie marcata implicit.
<form>
<h5 align="center">Vreau acasa:
<input type="checkbox" checked name="acasa">
Vreau la scoala:
<input type="checkbox" name="scoala">
</h5>
</form>
Vreau acasa: Vreau la scoala:
d.HIDDEN (cimp ascuns)permite transmiterea informatiilor programului care prelucreaza datele-utilizator,fara ca utilizatorul sa le vada pe ecran.De obicei se foloseste în cazul în care pagina Web
continind formularul este generata automat de un script CGI.
e.RADIO (buton radio)afiseaza un buton de interblocare;diferitele butoane radio care au acelasi atribut NAME= valoare sunt grupate în mod automat,astfel încât doar un singur buton din grup poate fi selectat.
<form><p>
Esti:
desteapta <input type="radio" name="tu">
frumoasa <input type="radio" name="tu" checked>
gospodina <input type="radio" name="tu"> </p> </form>
Top of Form
Esti: desteapta frumoasa gospodina
j.SUBMIT (transmitere),afiseaza un buton de submisie ainformatiilor introduse in formular.Atunci cind este apasat, transmite continutul intregului formular la serverul Web spre a fi prelucrat.
<form method="get"
action="http://www.infoiasi.ro/fcs/search/search.pl.cgi">
<p>Cauta in cadrul paginilor Facultatii de Informatica:</p>
<input name="Terms" size="50" maxlength="100" value="">
<input type="submit" value="Search">
</form>
Cauta in cadrul paginilor Facultatii de Informatica:
k.IMAGE este identic cu SUBMIT numai ca in locul unui buton permite specificarea unei imagini care atunci cind va fi apasata va trimite datele catre serverul Web.
l.RESET (reseteaza)permite utilizatorilor sa stearga continutul tuturor cimpurilor formularului.
<form>
<center>
Numele:
<input type="text" name="nume" size="20" value="">
<br>
Virsta:
<input type="text" name="virsta" size="2" value="">
<br>
Sinteti genial:
<input type="checkbox" name="geniu">
<hr width="400">
<input type="submit" value="Trimite formularul" size="25">
<br>
<br>
<input type="reset" value="Sterge formularul" size="25">
</center>
</form>
m.FILE (fisier)ofera osibilitatea de a ermite utilizatorilor sa transmita serverului Web un fisier (actiunea de upload ).
<p>Dati un nume de fisier sau alegeti unul:
<form>
<input type="file" name="atasament" size="30">
</form>
Dati un nume de fisier sau alegeti unul:
Numele:
Virsta:
Sinteti genial:
Dati un nume de fisier sau alegeti unul:
Efectul apasarii butonul Browse poate fi urmarit in figura de mai jos:
Anexa 3. Script CGI – Perl (pentru accesarea și actualizarea unei baze de date)
#!/usr/bin/perl -w
use CGI;
# variabilele referitoare la baza de date
my $DBHOST = "";
my $DBNAME= "ioan";
my $DBUSER = "ioan";
my $DBPASS = "parola1";
my $QUERY;
my ($dbh, $sth, $rc);
my ($a, $b, $c, $d, $e, $f, $g, $h, $i, $j, $k, $l, $m, $n, $o, $p, $q, $r);
my ($name, $clientname, $client);
# utilizam modulele CGI
use CGI qw(:standard);
use CGI::Carp qw(fatalsToBrowser);
# preluam numele clientului
$client=param('name');
# scriem antetul paginii Web
print<<"HTML";
Content-type: text/html
<html>
<body bgcolor="gray">
<form action="search.pl.cgi" method="get">
<table width="90%" bgcolor="#FFFFFF" border="1" bordercolor="blue" align="center">
<tr><td colspan="5">
HTML
# utilizam modulul DBI
use DBI;
$dbh = DBI->connect("dbi:mysql:dbname=$DBNAME;host=$DBHOST","$DBUSER", "$DBPASS",
{ PrintError => 0,
RaiseError => 0,
AutoCommit => 1})
|| die("Cannot connect to $DBNAME: $DBI::errstr");
# ne conectam la baza de date $DBNAME, de pe serverul $DBHOST utilizind
# username-ul $DBUSER si parola $DBPASS, prin driverul mysql.
# in cazul in care conectarea s-a desfasurat fara probleme
# atunci functia de conectare ne va returna un Database Handler nenul
# pregatim de executie interogarea SQL
$sth = $dbh->prepare("SELECT * FROM pictori ORDER BY nume")
|| die "Cannot prepare : $DBI::errstr\n";
$rc = $sth->execute()
|| die "Cannot execute : $DBI::errstr\n";
# selectam toti clientii din baza de date pentru a-i afisa intr-un formular de
# unde vor fi selectati in vederea aflarii diferitelor informatii despre ei
print << "HTML";
<center>
<b>Nume client</b>: <select name="name">
HTML
while (($clientname)=$sth->fetchrow_array){
print << "HTML";
<option value="$clientname">$clientname</option>
HTML
}
print << "HTML";
</select>
<input name="enterbutton" type="submit" value="Afiseaza">
<input name="toti" type="submit" value="Toti Pictorii">
<input name="sterg" type="submit" value="Sterge Logic">
<br>
<hr>
<p><b>Toti pictorii intre anii :</b>
<input type="" name="unu"> <input type="" name="doi">
<input type="submit" name="ca" value="Cauta"></p>
<hr><p>
<b>Nume Prenume:<input type="" name="nume"> An nastere:<input type="" name="ann">
An moarte:<input type="" name="anm">
Curentul:<input type="" name="curent"> </b><input type="submit" name="add" value="Adauga"></p>
<hr><p><FONT color=red><b>NU UITATI SA SELECTATI CLIENTUL
MAI INTAI</b></font><BR>
<b>Nume Prenume nou:<input type="" name="nume1"> An
nastere nou:<input type="" name="ann1">
An moarte nou:<input type="" name="anm1">
Curentul nou:<input type="" name="curent1"> </b><input type="submit" name="mod" value="Modifica"></p>
</center>
</td>
</tr>
HTML
# acest CGI poate fi apelat fara nici un parametru, caz in care va afisa
# un meniu din care sa se selecteze un eventual client
# daca scriptul primeste un sir in parametrul "name" atunci vor fi
# afisate informatii despre acel client (daca exista)
#if (defined($client) && $client ne ""){
$sth = $dbh->prepare("SELECT nume, ann, anm,
curent FROM pictori WHERE nume=?")
|| die "Cannot prepare : $DBI::errstr\n";
$rc = $sth->execute($client)
|| die "Cannot execute : $DBI::errstr\n";
# memoram in variabile datele returnate de server
my ($nume,
$ann,
$anm, $curent) = $sth->fetchrow_array;
print << "HTML";
<tr><td colspan="5" align="center">
<b>
Client: $client<br>
</b>
</td></tr>
<tr>
<th><font color=blue size=3><b>Nume Prenume Pictor</b></font></th>
<th><font color=blue size=3><b>Anul nasterii</b></font></th>
<th><font color=blue size=3><b>Anul mortii</b></font></th>
<th><font color=blue size=3><b>Curentul</b></font></th></tr>
<tr>
HTML
$a = CGI::param("toti");
if($a eq "Toti Pictorii")
{
$sth = $dbh->prepare("SELECT nume,
ann,
anm,
curent FROM pictori")
|| die "Cannot prepare : $DBI::errstr\n";
$rc = $sth->execute()
|| die "Cannot execute : $DBI::errstr\n";
# memoram in variabile datele returnate de server
my ($nume,
$ann,
$anm,
$curent) = $sth->fetchrow_array;
while (($nume,$ann,$anm,$curent)=$sth->fetchrow_array){
print<< "HTML";
<td align="center">$nume</td>
<td align="center">$ann</td>
<td align="center">$anm</td>
<td align="center">$curent</td>
</tr>
HTML
}
}
$b = CGI::param("enterbutton");
if($b eq "Afiseaza")
{
print<< "HTML";
<td align="center">$nume</td>
<td align="center">$ann</td>
<td align="center">$anm</td>
<td align="center">$curent</td></tr>
HTML
}
$c= CGI::param("ca");
$d= CGI::param("unu");
$e= CGI::param("doi");
if($c eq "Cauta")
{
if(($d eq "")&&($e eq ""))
{
print<< "HTML";
<script language=JavaScript>alert(" Va rog completati toate campurile !!!")</script>
HTML
}
else
{
$sth = $dbh->prepare("SELECT nume,ann,anm,curent FROM pictori WHERE ann>=$d AND anm<=$e")
|| die "Cannot prepare : $DBI::errstr\n";
$rc = $sth->execute()
|| die "Cannot execute : $DBI::errstr\n";
#memoram in variabile datele returnate de server
my ($nume,$ann,$anm,$curent) = $sth->fetchrow_array;
while (($nume,$ann,$anm,$curent)=$sth->fetchrow_array){
print<< "HTML";
<td align="center">$nume</td>
<td align="center">$ann</td>
<td align="center">$anm</td>
<td align="center">$curent</td></tr>
HTML
} #end while
}
}
$f= CGI::param("nume");
$g= CGI::param("ann");
$h= CGI::param("anm");
$i= CGI::param("curent");
$j= CGI::param("add");
if($j eq "Adauga")
{
if(($f eq "")||($g eq "")||($h eq "")||($i eq ""))
{
print<< "HTML";
<script language=JavaScript>alert(" Va rog completati
toate campurile !!!")</script>
HTML
}
else
{
$sth= $dbh->prepare("INSERT INTO pictori VALUES ('$f',$g,$h,'$i')")
|| die "Cannot prepare : $DBI::errstr\n";
$rc = $sth->execute()
|| die "Cannot execute : $DBI::errstr\n";
print<< "HTML";
<script language=JavaScript>alert("Adugare cu succes!!! NUME: $f ANN: $g ANM: $h CURENT: $i")</script>
HTML
}
}
$k= CGI::param("sterg");
$l= CGI::param("name");
if($k eq "Sterge Logic")
{
$sth = $dbh->prepare("DELETE FROM pictori WHERE nume='$l'")
|| die "Cannot prepare : $DBI::errstr\n";
$rc = $sth->execute()
|| die "Cannot execute : $DBI::errstr\n";
print<< "HTML";
<script language=JavaScript>alert("Pictorul $l a fost
halibardit !!!")</script>
HTML
}
#$r= CGI::param("sterg1");
#if($r eq "Sterge Absolut")
# {
# $sth = $dbh->prepare("PACK pictori")
# || die "Cannot prepare : $DBI::errstr\n";
# $rc = $sth->execute()
# || die "Cannot execute : $DBI::errstr\n";
# }
$m= CGI::param("nume1");
$n= CGI::param("ann1");
$o= CGI::param("anm1");
$p= CGI::param("curent1");
$q= CGI::param("mod");
if($q eq "Modifica")
{
if(($m eq "")||($n eq "")||($o eq "")||($p eq ""))
{
print<< "HTML";
<script language=JavaScript>alert("Completati toate
campurile !!!")</script>
HTML
}
else
{
$sth=$dbh->prepare("UPDATE pictori SET nume='$m',ann=$n,
anm=$o,curent='$p' WHERE nume='$l'")
|| die "Cannot prepare : $DBI::errstr\n";
$rc = $sth->execute()
|| die "Cannot execute : $DBI::errstr\n";
print<< "HTML";
<script language=JavaScript>alert("Modificare cu succes!!! NUME:$m ANN: $n ANM: $o CURENT: $p")</s
HTML
}
}
print << "HTML";
</table></form></body></html>
HTML
# am afisat informatiile, putem iesi din aplicatie
$sth->finish();$dbh->disconnect;
Bibliografie
[1] Sabin Corneliu Buraga, Sisteme de operare. Rețele de calculatoare (2002) – note de curs, Faculatatea de Informatică, Univ. ,,Al. I. Cuza”,Iași, cursuri postuniversitare.
[2] Ștefan Andrei, Programare în Java (2002) – note de curs, Faculatatea de Informatică, Univ. ,,Al. I. Cuza”,Iași, cursuri postuniversitare.
[3] S.C., Buraga, Ș., Andrei, CGI-uri versus servlet-uri – revista NET Report, dec. 2001
[4] Ștefan Andrei, Programare în C, C++ (2002) – curs, Faculatatea de Informatică, Univ. ,,Al. I. Cuza”,Iași, cursuri postuniversitare.
[5] Sabin Corneliu Buraga, Tehnologii Web, Ed. Matrix, București, 2001:
http://www.infoiasi.ro/~busaco/books/web.html
[6] Alexandru Preoteasa, Swing – revista PC Report, Atelier, nr.72, septembrie 1998
[7] Zoldi Arpad, Servlet-urile – un altfel de aplicații Java, [anonimizat]
Copyright Notice
© Licențiada.org respectă drepturile de proprietate intelectuală și așteaptă ca toți utilizatorii să facă același lucru. Dacă consideri că un conținut de pe site încalcă drepturile tale de autor, te rugăm să trimiți o notificare DMCA.
Acest articol: . Generarea Dinamica a Paginilor Web Folosind Tehnologia Servlet (ID: 148969)
Dacă considerați că acest conținut vă încalcă drepturile de autor, vă rugăm să depuneți o cerere pe pagina noastră Copyright Takedown.
