====== Laborator 04 ======
====== Realizarea de aplicații web folosind Java Servlets ======
===== Obiective ====
* descrierea tipurilor de aplicații web ce pot fi implementate folosind Java Enterprise Edition și a modului în care sunt gestionate cererile și răspunsurile interschimbate între client și server;
* înțelegerea arhitecturii serverului HTTP Apache Tomcat, a funcționalității pe care o pune la dispoziție și a modului în care pot fi dezvoltate aplicații web ce folosesc tehnologia Java Servlets în contextul său;
* proiectarea de aplicații web pe baza Java Servlets, prin realizarea operațiilor corespunzătoare ciclului de viață (inițializare, execuție, distrugere);
* dezvoltarea de aplicații web ce utilizează tehnologia Java Servlets folosind medii integrate de dezvoltare (Eclipse / NetBeans);
* implementarea unor funcționalități complexe ale aplicațiilor web, conform specificațiilor funcționale (filtre, invocarea altor resurse web, încărcarea de fișiere, procesare asincronă, operații I/O non-blocante);
* integrarea aplicațiilor web cu sisteme de gestiune pentru baze de date pentru asigurarea persistenței informațiilor;
* gestionarea stării aplicației web folosind diferite mecanisme (câmpuri ascunse, rescrierea URL-urilor, cookies, sesiuni).
===== Cuvinte Cheie ====
Java Servlets, Apache Tomcat 8.x, web application, Java Enterprise Edition, request, response, servlet life cycle, servlet execution flow, filter / filter chain, request dispatcher, file upload, asynchronous processing, non-blocking I/O, DBMS integration, hidden fields, URL rewriting, cookie, session
===== Materiale Ajutătoare ======
TODO
===== Implementarea aplicaţiilor web folosind Java Enterprise Edition =====
O aplicaţie web este definită ca o **extensie dinamică a unui server** (web sau de aplicaţii).
Frecvent, acestea sunt clasificate ca:
- **orientate pe prezentare**, în cazul în care sunt generate pagini Internet interactive descrise folosind diferite limbaje de adnotare (HTML, XML) având conţinut dinamic ca răspuns la cererile formulate de utilizatori; pentru acest tip de servicii se utilizează de obicei tehnologiile Facelets sau JavaServer Faces;
- **orientate pe servicii**, în situaţia în care implementează funcţionalitatea unui serviciu web; tehnologia Java Servlets este mai adecvată dezvoltării acestui tip de aplicaţii web, fiind totodată utilizată şi pentru gestiunea datelor nontextuale şi a mecanismelor de control a altor tipuri de aplicaţii web.
De cele mai multe ori, o aplicaţie web orientată pe prezentare exploatează funcţionalitatea unei aplicaţii web orientată pe servicii.
În Java Enterprise Edition, o astfel de funcţionalitate este implementată prin **componente web**, acestea putând fi Java Servlets, pagini Internet dezvoltate cu tehnologiile JSF (JavaServer Faces) / JSP (Java Server Pages), respectiv servicii web (implementate cu JAX-WS sau JAX-RS). Capabilităţile componentelor web sunt îmbogăţite prin serviciile pe care le oferă containerul în contextul căruia rulează, cum ar fi gestiunea cererilor, securitate, concurenţă şi gestiunea ciclului de viaţă, oferind acces la interfeţe de programare ca posibilitatea accesării lor prin intermediul unei denumiri, poştă electronică, tranzacţii.
Pe lângă acestea, aplicaţia web conţine resurse statice (imagini, foi de stil), clase ajutătoare sau diferite biblioteci.
Cererile HTTP formulate de clienţi şi transmise prin intermediul unui browser sunt convertite de serverul web (ce implementează tehnologiile Java Servlet şi JavaServer Pages) într-un obiect [[http://docs.oracle.com/javaee/7/api/javax/servlet/http/HttpServletRequest.html|HttpServletRequest]], fiind ulterior transmis componentei web ce poate interacționa cu o clasă JavaBeans sau cu o bază de date pentru a genera conţinutul dinamic. Răspunsul obţinut poate fi transmis altei componente web sau va fi convertit într-un obiect [[http://docs.oracle.com/javaee/7/api/javax/servlet/http/HttpServletResponse.html|HttpServletResponse]], fiind apoi transformat într-un răspuns HTTP care este afişat mai departe în browser.
{{ :laboratoare:laborator04:01javaservlets_flow.png?nolink }}
Unele aspecte ale comportamentului aplicaţiilor web pot fi configurate atunci când aceasta este instalată (//eng.// deploy) în contextul container-ului fie prin intermediul unor adnotări Java, fie în fişiere XML (descriptorul de instalare al aplicaţiei web) care trebuie să respecte schemele specificaţiei Java Servlet.
===== Tehnologia Java Servlets - aspecte generale =====
Tehnologia Java Servlets furnizează un mecanism prin care pot fi dezvoltate aplicaţii web folosind limbajul de programare Java, reprezentând o alternativă pentru CGI (Common Gateway Interface), caracterizată prin dependența de platformă (programele fiind scrise în C) și lipsă de scalabilitate (performanțe scăzute în contextul în care trebuiau oferite răspunsuri pentru mai multe cereri transmise simultan). Aceste inconveniente au fost adresate în mod deosebit în cadrul tehnologiei Java Servlets.
Avantajele utilizării Java Servlets includ:
* eficienţa (iniţializarea se face doar la încărcarea unui servlet, cererile fiind tratate prin apelarea metodei [[http://docs.oracle.com/javaee/7/api/javax/servlet/GenericServlet.html#service-javax.servlet.ServletRequest-javax.servlet.ServletResponse-|service()]]);
* persistenţa (după ce este încărcat, obiectele unui servlet – ce pot conţine informaţii din baza de date – sunt disponibile cât timp acesta este în execuţie, ceea ce asigură o performanţă ridicată comparativ cu încărcarea din baza de date a informaţiilor fiecare dată);
* portabilitate (asigurată de limbajul de programare Java, astfel încât platforma pe care rulează aplicaţia poate fi schimbată fără modificarea codului sursă);
* robusteţe (acces la toate mecanismele oferite de limbajul de programare Java – cu o ierarhie de excepţii pentru tratarea erorilor şi colectarea memoriei disponibile);
* extensibilitate (fiind dezvoltat într-un limbaj orientat obiect, un servlet poate fi extins potrivit cerinţelor aplicaţiei);
* securitate (conform modelului de securitate implementat în Java).
Un servlet reprezintă o clasă implementată în limbajul de programare Java, utilizată pentru a extinde capabilităţile unui server care găzduieşte aplicaţii accesate conform **modelului cerere-răspuns**. Cea mai frecvent întâlnită funcţionalitate în legătură cu un servlet este legată de aplicaţiile web, cu toate că acesta poate răspunde oricărui tip de cereri. Prin urmare tehnologia Java Servlet defineşte clase servlet adaptate protocolului HTTP.
Pachetele ''javax.servlet'' şi ''javax.servlet.http'' oferă interfeţe şi clase pentru scrierea de Java Servlets. Orice servlet trebuie să implementeze interfaţa [[http://docs.oracle.com/javaee/7/api/javax/servlet/Servlet.html|javax.servlet.Servlet]] care defineşte metodele ce caracterizează ciclul de viaţă al unui astfel de obiect. Când este implementat un serviciu generic, se poate folosi (sau extinde) clasa [[http://docs.oracle.com/javaee/7/api/javax/servlet/GenericServlet.html|javax.servlet.GenericServlet]]. Clasa [[http://docs.oracle.com/javaee/7/api/javax/servlet/http/HttpServlet.html|javax.servlet.http.HttpServlet]] oferă metode precum ''doGet()'' şi ''doPost()'' pentru a trata servicii specifice protocolului HTTP.
{{ :laboratoare:laborator04:02javaservlets_classhierarchy.png?nolink&200 }}
==== Ciclul de viață al unui Java Servlet ====
Ciclul de viaţă al unui Java Servlet este controlat de containerul în care a fost configurat servlet-ul. Atunci când o cerere este asociată unui servlet, container-ul care conţine servlet-ul realizează următoarele acţiuni:
- dacă nu există o instanţă a servlet-ului:
- încarcă clasa servlet;
- creează o instanţă a clasei servlet;
- iniţializează instanţa clasei servlet prin apelarea metodei ''init()'';
- apelează metoda ''service()'' având ca parametrii obiecte cerere şi răspuns.
În condiţiile în care este necesară ştergerea servlet-ului, este apelată metoda ''destroy()'' a acestuia.
{{ :laboratoare:laborator04:03javaservlets_lifecycle.png?nolink&700 }}
Se pot defini clase ascultător (implementări ale unor interfeţe ascultător), adnotate cu însemnarea ''@WebListener'' spre a monitoriza evenimente corespunzătoare ciclului de viaţă al unui obiect Java Servlet. Metodele asociate vor primi ca argumente parametrii ce conţin informaţii referitoare la evenimentul respectiv.
^ Obiect ^ Eveniment ^ Interfaţă (ce trebuie) Implementată ^ Obiect Eveniment ^
| context web | creare, distrugere | ''javax.servlet.ServletContextListener'' | ''ServletContextEvent'' |
| ::: | operaţii asupra atributelor (CRUD) | ''javax.servlet.ServletContextAttributeListener'' | ''ServletContextAttributeEvent'' |
| sesiune | creare, invalidare, activare, pasivizare, expirare | ''javax.servlet.http.HttpSessionListener'' ''javax.servlet.http.HttpSessionActivationListener'' | ''HttpSessionEvent'' |
| ::: | operaţii asupra atributelor (CRUD) | ''javax.servlet.http.HttpSessionAttributeListener'' | ''HttpSessionBindingEvent'' |
| cerere | cererea pentru un obiect servlet este procesată de componentele web | ''javax.servlet.ServletRequestListener'' | ''ServletRequestEvent'' |
| ::: | operaţii asupra atributelor (CRUD) | ''javax.servlet.ServletRequestAtrributeListener'' | ''ServletRequestAttributeEvent'' |
==== Structura unui Java Servlet ====
Un Java Servlet este o clasă derivată din [[http://docs.oracle.com/javaee/7/api/javax/servlet/http/HttpServlet.html|javax.servlet.http.HttpServlet]], suprascriind metodele [[http://docs.oracle.com/javaee/7/api/javax/servlet/GenericServlet.html#init-javax.servlet.ServletConfig-|init()]], [[http://docs.oracle.com/javaee/7/api/javax/servlet/GenericServlet.html#destroy--|destroy()]] (pentru operaţiile realizate la construirea şi distrugerea obiectului instanţă a clasei), respectiv (cel mai frecvent) [[http://docs.oracle.com/javaee/7/api/javax/servlet/http/HttpServlet.html#doPost-javax.servlet.http.HttpServletRequest-javax.servlet.http.HttpServletResponse-|doPost()]] şi [[http://docs.oracle.com/javaee/7/api/javax/servlet/http/HttpServlet.html#doGet-javax.servlet.http.HttpServletRequest-javax.servlet.http.HttpServletResponse-|doGet()]] pentru gestiunea interacţiunii cu utilizatorul (astfel de operații fiind utile mai ales atunci când se folosesc formulare, astfel încât comunicaţia dintre server şi client transmite informaţii prin intermediul acestora).
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Enumeration;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet("/SampleServlet")
public class SampleServlet extends HttpServlet {
final public static long serialVersionUID = 1024L;
@Override
public void init(ServletConfig config) throws ServletException {
super.init(config);
}
@Override
public void destroy() {
}
@Override
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
ArrayList values = new ArrayList<>();
Enumeration parameters = request.getParameterNames();
while(parameters.hasMoreElements()) {
String parameter = (String)parameters.nextElement();
if (parameter.contains("...")) {
values.add(request.getParameter(parameter)));
}
}
response.setContentType("text/html");
PrintWriter printWriter = new PrintWriter(response.getWriter());
displayPage(printWriter);
printWriter.close();
}
@Override
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// ...
}
}
Adnotarea [[http://docs.oracle.com/javaee/7/api/javax/servlet/annotation/WebServlet.html|@WebServlet]] defineşte clasa Java Servlet într-o aplicaţie web, conţinând metadate despre aceasta. Ea trebuie să specifice cel puţin un URL (prin câmpurile [[http://docs.oracle.com/javaee/7/api/javax/servlet/annotation/WebServlet.html#urlPatterns--|urlPatterns]] sau [[http://docs.oracle.com/javaee/7/api/javax/servlet/annotation/WebServlet.html#value--|value]]), celelalte atribute fiind opţionale, iniţializându-se cu valorile implicite.
Campul ''urlPatterns'' se foloseşte atunci când sunt specificate mai multe atribute, în timp ce câmpul ''value'' este folosit atunci când se specifică doar URL-ul.
Container-ul iniţializează un obiect Java Servlet (apelând metoda ''init()'') după ce îl încarcă şi îl instanţiază, dar înainte de a accepta invocări de la clienţi. Se permite astfel încărcarea de informaţii persistente (date de configurare), iniţializarea resurselor şi realizarea de operaţii care sunt executate o singură dată. Alternativ se poate folosi atributul [[http://docs.oracle.com/javaee/7/api/javax/servlet/annotation/WebServlet.html#initParams--|initParams]] al adnotării ''@WebServlet'' ce conţine la rândul său o adnotare [[http://docs.oracle.com/javaee/7/api/javax/servlet/annotation/WebInitParam.html|@WebInitParam]].
Procesul de iniţializare permite obţinerea de informaţii care sunt utile doar obiectului servlet în cauză. Pentru obţinerea de date care sunt disponibile tuturor componentelor aplicaţiei web sunt folosiţi parametrii ai contextului. Cel mai frecvent, în cadrul acestui proces este realizată conexiunea către sistemul de gestiune al bazei de date.
În situaţia în care nu se reuşeşte terminarea cu succes a operaţiei de iniţializare, este generată o excepţie de tipul [[http://docs.oracle.com/javaee/7/api/javax/servlet/UnavailableException.html|UnavailableException]].
Funcţionalitatea pusă la dispoziţie de către un servlet este implementată în metoda ''service()'' a clasei ''GenericServlet'', prin apelarea metodelor corespunzătoare ''doMethod()'' (unde ''Method'' poate avea valorile ''Get'', ''Post'', ''Put'', ''Delete'', ''Options'' sau ''Trace'') ale unui obiect ''HttpServlet'' sau prin metode specifice protocolului definite de o clasă care implementează interfaţa ''Servlet''. O metodă serviciu va primi informaţiile din cerere (obiect tip ''HttpServletRequest'') apelând ''getParameterNames()'' / ''getParameter()'' şi va transmite informaţiile în răspuns (obiect tip ''HttpServletResponse'') – folosind un obiect ''PrintWriter'' asociat acestuia (obţinut prin metoda ''getWriter()'').
Denumirea de metodă serviciu este folosită pentru orice metodă dintr-o clasă servlet care oferă o funcţionalitate pentru un client.Nu este necesară implementarea metodei ''service()'' (apelată atunci când există o instanţă a clasei ''Servlet''), deoarece este definită în clasa abstractă ''HttpServlet'', implementarea apelând mai departe metoda responsabilă de tipul de cerere HTTP. Aşadar, prin clasa ''HttpServlet'' pot fi creaţi servleţi HTTP care să funcţioneze în contextul unei aplicaţii web.
public abstract class HttpServlet extends GenericServlet implements java.io.Serializable
Prin urmare, orice clasă derivată din ''HttpServlet'' trebuie să implementeze cel puţin una dintre metodele:
* [[http://docs.oracle.com/javaee/7/api/javax/servlet/http/HttpServlet.html#doGet-javax.servlet.http.HttpServletRequest-javax.servlet.http.HttpServletResponse-|doGet()]], dacă servlet-ul tratează cereri ''HTTP GET'';
* [[http://docs.oracle.com/javaee/7/api/javax/servlet/http/HttpServlet.html#doPost-javax.servlet.http.HttpServletRequest-javax.servlet.http.HttpServletResponse-|doPost()]], dacă servlet-ul tratează cereri ''HTTP POST'';
* [[http://docs.oracle.com/javaee/7/api/javax/servlet/http/HttpServlet.html#doPut-javax.servlet.http.HttpServletRequest-javax.servlet.http.HttpServletResponse-|doPut()]], dacă servlet-ul tratează cereri ''HTTP PUT'';
* [[http://docs.oracle.com/javaee/7/api/javax/servlet/http/HttpServlet.html#doDelete-javax.servlet.http.HttpServletRequest-javax.servlet.http.HttpServletResponse-|doDelete()]], dacă servlet-ul tratează cereri ''HTTP DELETE'';
De regulă, metodele [[http://docs.oracle.com/javaee/7/api/javax/servlet/http/HttpServlet.html#doOptions-javax.servlet.http.HttpServletRequest-javax.servlet.http.HttpServletResponse-|doOptions()]] şi [[http://docs.oracle.com/javaee/7/api/javax/servlet/http/HttpServlet.html#doTrace-javax.servlet.http.HttpServletRequest-javax.servlet.http.HttpServletResponse-|doTrace()]] sunt mai puţin utilizate.
Totodată, este recomandat să se implementeze şi metodele:
* ''init()'' şi ''destroy()'', pentru a gestiona resursele alocate în timpul în care servlet-ul este în execuţie;
În general, în metodele ''init()'' şi ''destroy()'' sunt alocate şi dezalocate resurse partajate cum ar fi conexiunea la baza de date sau accesul la fişiere.
* [[http://docs.oracle.com/javaee/7/api/javax/servlet/GenericServlet.html#getServletInfo--|getServletInfo()]], folosit de servlet pentru a oferi informaţii despre el.
{{ :laboratoare:laborator04:04javaservlets_methods.png.png?nolink&400 }}
O cerere conţine datele transmise de la client către servlet şi trebuie să implementeze interfaţa [[http://docs.oracle.com/javaee/7/api/javax/servlet/ServletRequest.html|javax.servlet.ServletRequest]], unde sunt definite metode pentru accesarea de:
* parametri (pentru comunicarea de informaţii între clienţi / servleţi);
* atribute – instanţe ale unor obiecte (folosite la comunicarea dintre un container al unui servlet şi servlet sau pentru comunicarea între mai mulţi servleţi);
* informaţii despre:
* protocolul prin care este transmisă cererea;
* locaţie.
Aceste date pot fi analizate manual (parsarea unui flux de date) folosind un obiect ''BufferedReader'' creat dintr-un obiect [[http://docs.oracle.com/javaee/7/api/javax/servlet/ServletInputStream.html|ServletInputStream]] întors de metoda [[http://docs.oracle.com/javaee/7/api/javax/servlet/ServletRequest.html#getInputStream--|getInputStream()]].
Obiectul [[http://docs.oracle.com/javaee/7/api/javax/servlet/http/HttpServletRequest.html|javax.servlet.http.HttpServletRequest]] transmis prin cerere conţine URL-ul cererii, antetele HTTP şi interogarea formată din perechi de parametri şi valori.
URL-ul cererii are forma %%http://[adresa]:[port]/[cale]?[interogare]%%, unde ''cale'' conţine, la rândul ei, calea contextuală (contexul aplicaţiei web în care rulează servlet-ul), calea către servlet (care indică componenta care procesează cererea) precum şi alte informaţii. Acestea pot fi obţinute apelând metodele: [[http://docs.oracle.com/javaee/7/api/javax/servlet/http/HttpServletRequest.html#getContextPath--|getContextPath()]], [[http://docs.oracle.com/javaee/7/api/javax/servlet/http/HttpServletRequest.html#getServletPath--|getServletPath()]] şi [[http://docs.oracle.com/javaee/7/api/javax/servlet/http/HttpServletRequest.html#getPathInfo--|getPathInfo()]]. Interogarea este compusă dintr-un set de parametri şi valori ce pot fi obţinuţi din obiectul cerere folosind metodele [[http://docs.oracle.com/javaee/7/api/javax/servlet/ServletRequest.html#getParameterNames--|getParameterNames()]] şi [[http://docs.oracle.com/javaee/7/api/javax/servlet/ServletRequest.html#getParameter-java.lang.String-|getParameter()]]. Interogările pot să apară explicit în URL-ul cererii, fiind anexat acesteia când se foloseşte metoda ''HTTP GET'' sau poate fi conţinut în pagina Internet.
Obiectul ''HttpServletRequest'' conţine atât denumirile parametrilor transmişi (metoda ''getParameterNames()'') cât şi valoarea lor (metoda ''getParameter()'' ce primeşte ca argument identificatorul acestuia).
Metodele mai folosite ale clasei ''HttpServletRequest'' sunt moștenite din clasa ''ServletRequest'': [[http://docs.oracle.com/javaee/7/api/javax/servlet/ServletRequest.html#getContentType--|getContentType()]], [[http://docs.oracle.com/javaee/7/api/javax/servlet/ServletRequest.html#getInputStream--|getInputStream()]], [[http://docs.oracle.com/javaee/7/api/javax/servlet/ServletRequest.html#getParameter-java.lang.String-|getParameter()]], [[http://docs.oracle.com/javaee/7/api/javax/servlet/ServletRequest.html#getParameterNames--|getParameterNames()]], dar și proprii: [[http://docs.oracle.com/javaee/7/api/javax/servlet/http/HttpServletRequest.html#getCookies--|getCookies()]], [[http://docs.oracle.com/javaee/7/api/javax/servlet/http/HttpServletRequest.html#getHeaderNames--|getHeaderNames()]], [[http://docs.oracle.com/javaee/7/api/javax/servlet/http/HttpServletRequest.html#getHeaders-java.lang.String-|getHeaders()]], [[http://docs.oracle.com/javaee/7/api/javax/servlet/http/HttpServletRequest.html#getSession--|getSession()]], [[http://docs.oracle.com/javaee/7/api/javax/servlet/http/HttpServletRequest.html#getMethod--|getMethod()]].
Un răspuns conţine datele transmise de la servlet către client şi trebuie să implementeze interfaţa [[http://docs.oracle.com/javaee/7/api/javax/servlet/ServletResponse.html|javax.servlet.ServletResponse]], unde sunt definite metode pentru obţinerea unui flux prin care se poate realiza comunicarea cu clientul, indicându-se tipul de conţinut (prin metoda [[http://docs.oracle.com/javaee/7/api/javax/servlet/ServletResponse.html#setContentType-java.lang.String-|setContentType()]], al cărei parametru poate fi ''text/html''), dacă se aloca o zonă de memorie (metoda [[http://docs.oracle.com/javaee/7/api/javax/servlet/ServletResponse.html#setBufferSize-int-|setBufferSize()]] oferă o perioadă timp înainte ca mesajul să fie transmis la client, permiţând stabilirea unor coduri de stare sau a unor antete) şi informaţii despre locaţie.
Pentru transmiterea de date de tip caracter se foloseşte un obiect [[http://docs.oracle.com/javase/7/docs/api/java/io/PrintWriter.html?is-external=true|PrinterWriter]] întors de metoda [[http://docs.oracle.com/javaee/7/api/javax/servlet/ServletResponse.html#getWriter--|getWriter()]], în timp ce pentru transmiterea de date binare se foloseşte un obiect [[http://docs.oracle.com/javaee/7/api/javax/servlet/ServletOutputStream.html|ServletOutputStream]] întors de metoda [[http://docs.oracle.com/javaee/7/api/javax/servlet/ServletResponse.html#getOutputStream--|getOutputStream()]].
Obiectul ''HttpServletResponse'' are câmpuri care reprezintă antete HTTP ce conţin coduri de stare (pentru a indica motivele pentru care o cerere nu poate fi satisfăcută sau faptul că s-a realizat redirecţionarea către altă resursă) ca şi obiecte ce vor reţine informaţii specifice aplicaţiei (eventual, legate de sesiune) – //eng.// cookies, pe maşina clientului astfel încât persistenţa stării este realizată prin mecanisme implementate în afara serverului. Un astfel de mecanism este necesar datorită faptului că serverul nu dispune de alte posibilităţi pentru a identifica un client, în interacţiunea cu aplicaţia web pe care o găzduieşte.
Obiectul [[http://docs.oracle.com/javaee/7/api/javax/servlet/http/HttpServletResponse.html|javax.servlet.http.HttpServletResponse]] este folosit pentru construirea documentului care va transmis de la server către client, într-un obiect de tip ''PrintWriter''.
Metodele cele mai folosite din clasa ''HttpServletResponse'' sunt moştenite din clasa ''ServletResponse'': [[http://docs.oracle.com/javaee/7/api/javax/servlet/ServletResponse.html#flushBuffer--|flushBuffer()]], [[http://docs.oracle.com/javaee/7/api/javax/servlet/ServletResponse.html#getBufferSize--|getBufferSize()]], [[http://docs.oracle.com/javaee/7/api/javax/servlet/ServletResponse.html#setBufferSize-int-|setBufferSize()]], [[http://docs.oracle.com/javaee/7/api/javax/servlet/ServletResponse.html#getContentType--|getContentType()]], [[http://docs.oracle.com/javaee/7/api/javax/servlet/ServletResponse.html#setContentType-java.lang.String-|setContentType()]], [[http://docs.oracle.com/javaee/7/api/javax/servlet/ServletResponse.html#getOutputStream--|getOutputStream()]], [[http://docs.oracle.com/javaee/7/api/javax/servlet/ServletResponse.html#setCharacterEncoding-java.lang.String-|setCharacterEncoding()]], dar şi proprii: [[http://docs.oracle.com/javaee/7/api/javax/servlet/http/HttpServletResponse.html#addCookie-javax.servlet.http.Cookie-|addCookie()]], [[http://docs.oracle.com/javaee/7/api/javax/servlet/http/HttpServletResponse.html#getHeader-java.lang.String-|getHeader()]], [[http://docs.oracle.com/javaee/7/api/javax/servlet/http/HttpServletResponse.html#setHeader-java.lang.String-java.lang.String-|setHeader()]], [[http://docs.oracle.com/javaee/7/api/javax/servlet/http/HttpServletResponse.html#getStatus--|getStatus()]], [[http://docs.oracle.com/javaee/7/api/javax/servlet/http/HttpServletResponse.html#setStatus-int-|setStatus()]].
Un servlet se găseşte de regulă pe un server capabil să ruleze mai multe fire de execuţie, astfel încât în condiţiile unor cereri simultane, trebuie sincronizat accesul la resurse partajate, cum ar fi date din memorie (variabile tip instanţă sau clase) sau obiecte externe (fişiere, conexiuni la baze de date sau conexiuni în reţea).
Un container ce conţine un servlet poate determina distrugerea acestuia (în cazul în care se realizează colectarea memoriei disponibile sau atunci când acesta se închide), apelându-se metoda ''destroy()'' din interfaţa ''Servlet''. Aici toate resursele utilizate de către servlet trebuie eliberate, asigurându-se şi persistenţa prin reţinerea informaţiilor necesare în baza de date. Toate metodele serviciu corespunzătoare unui servlet trebuie să fie terminate atunci când container-ul urmează să îl distrugă. Astfel, metoda ''destroy()'' este apelată doar după ce toate metodele serviciu s-au terminat sau după expirarea unei anumite perioade stabilită de server. Trebuie ca toate firele de execuţie pe care sunt realizate operaţii de către server să se termine atunci când este apelată metoda ''destroy()''.
O soluţie ar fi contorizarea numărului de fire de execuţie care rulează şi invocarea ''Thread.sleep()'' cât timp acestea nu s-au terminat.
În exemplu, au fost preluaţi parametrii transmişi printr-un apel tip ''POST'' (presupunând că parametrii au fost specificaţi la crearea unui formular prin elemente de tip '''' - care nu au activat parametrul disabled, acestea nefiind transmise) şi reţinuţi în tabloul ''values'', de unde pot fi prelucraţi. De asemenea, construirea răspunsului către client se face în metoda ''displayPage()'', care primeşte un obiect de tip ''PrinterWriter'' în care se creează (prin apeluri ''println()'') pagina Internet care va fi afişată în browser.
===== Implementarea unor funcționalități complexe =====
==== Filtre ====
Un **filtru** este un obiect care poate transforma antetele şi/sau conţinutul unei cereri sau a unui răspuns. Funcţionalitatea sa poate fi ataşată oricărui tip de resursă web şi constă în:
* analiza unei cereri şi realizarea unor acțiuni în conformitate cu aceasta;
* blocarea unei cereri şi a răspunsului corespunzător pentru a fi prelucrate mai departe;
* modificarea antetelor şi/sau conţinutului unei cereri/răspuns, construind o versiune particularizată a acesteia;
* interacţiunea cu resurse externe.
Un obiect care funcţionează ca filtru al unei resurse web nu poate avea dependenţe faţă de aceasta.
De regulă, filtrele sunt folosite în procesul de autentificare, jurnalizare, conversie de imagini, compresie a datelor, criptare, parsarea fluxurilor de date, transformări XML. O resursă web poate fi filtrată printr-un lanţ ce conţine 0, 1 sau mai multe filtre într-o anumită ordine.
În pachetul ''javax.servlet'', filtrele sunt definite prin interfeţele [[https://docs.oracle.com/javaee/7/api/javax/servlet/Filter.html|Filter]], [[https://docs.oracle.com/javaee/7/api/javax/servlet/FilterChain.html|FilterChain]] şi [[https://docs.oracle.com/javaee/7/api/javax/servlet/FilterConfig.html|FilterConfig]].
Un filtru implementează interfaţa ''javax.servlet.Filter'' şi este adnotat cu însemnarea [[http://docs.oracle.com/javaee/7/api/javax/servlet/annotation/WebFilter.html|@WebFilter]] care trebuie să specifice cel puţin un URL (folosind atributele [[http://docs.oracle.com/javaee/7/api/javax/servlet/annotation/WebFilter.html#urlPatterns--|urlPatterns]] sau [[http://docs.oracle.com/javaee/7/api/javax/servlet/annotation/WebFilter.html#value--|value]]. Informaţiile cu privire la configurarea unui filtru sunt precizate prin atributul [[http://docs.oracle.com/javaee/7/api/javax/servlet/annotation/WebFilter.html#initParams--|initParams]] al aceleiaşi adnotări. Metoda cea mai importantă a unui filtru este [[https://docs.oracle.com/javaee/7/api/javax/servlet/Filter.html#doFilter-javax.servlet.ServletRequest-javax.servlet.ServletResponse-javax.servlet.FilterChain-|doFilter()]] care primeşte cererea, răspunsul şi lanţul de filtre, în afară de ea trebuind implementate metodele ''init()'' şi ''destroy()''. Metoda ''doFilter()'' poate analiza antetele cererii şi răspunsului (doar după invocarea următorului filtru din lanţul de filtre), poate particulariza obiectele cerere şi răspuns în sensul modificării antetelor sau conţinutului, poate invoca următorul filtru din lanţul de filtre (prin apelarea metodei ''doFilter()'' a acestuia şi transmiterea cererii şi răspunsului primite sau prelucrate de el), respectiv blocarea comunicaţiei, generarea unei excepţii pentru a indica producerea unei erori în cadrul procesării.
Un filtru poate adăuga un atribut la cerere sau poate introduce informaţii în contextul unui răspuns. Pentru a suprascrie metodele cererii, obiectul de tip cerere va fi împachetat într-un obiect derivat din [[http://docs.oracle.com/javaee/7/api/javax/servlet/ServletRequestWrapper.html|ServletRequestWrapper]] sau [[http://docs.oracle.com/javaee/7/api/javax/servlet/http/HttpServletRequestWrapper.html|HttpServletRequestWrapper]]. Similar, pentru răspuns va fi folosit [[http://docs.oracle.com/javaee/7/api/javax/servlet/ServletResponseWrapper.html|ServletResponseWrapper]] sau [[http://docs.oracle.com/javaee/7/api/javax/servlet/http/HttpServletResponseWrapper.html|HttpServletResponseWrapper]].
Un container foloseşte asocieri între filtre şi resurse web (prin nume sau prin URL) pentru a determina modul (precum şi ordinea) în care vor fi aplicate. În scopuri de jurnalizare, poate fi folosită masca ''%%/*%%'', aplicându-se tuturor evenimentelor ce implică comunicaţie între client şi server. Un filtru poate fi asociat mai multor resurse web şi o resursă web poate avea asociate mai multe filtre (un lanţ de filtre).
==== Invocarea altor resurse web ====
**Invocarea altor resurse web** poate fi realizată:
* direct, incluzând conţinutul altei resurse sau transmiţând mai departe cererea către o altă resursă;
* indirect, încorporând un URL care referă o altă componentă web în răspunsul transmis către client.
Pentru a invoca o resursă disponibilă pe serverul pe care rulează componenta web, trebuie obţinut un obiect [[https://docs.oracle.com/javaee/7/api/javax/servlet/RequestDispatcher.html|RequestDispatcher]] folosindu-se metoda [[https://docs.oracle.com/javaee/7/api/javax/servlet/ServletContext.html#getRequestDispatcher-java.lang.String-|getRequestDispatcher()]] (ce primeşte ca parametru URL-ul resursei ce va fi invocată) aplicată fie pe obiectul cerere, fie pe contextul web. URL-ul resursei va fi o cale relativă, în situaţia în care se apelează din contextul cererii, respectiv o cale absolută, în cazul în care se apelează pe contextul web. În cazul în care resursa nu este disponibilă pe server sau serverul nu implementează un obiect ''RequestDispatcher'' pentru tipul respectiv de resursă, metoda ''getRequestDispatcher()'' va întoarce ''null''.
Includerea unei alte resurse web (header, footer, informaţii de copyright, meniuri) în răspunsul construit de o componentă web se face folosind metoda [[https://docs.oracle.com/javaee/7/api/javax/servlet/RequestDispatcher.html#include-javax.servlet.ServletRequest-javax.servlet.ServletResponse-|include()]] a obiectului ''RequestDispatcher''. Dacă resursa este o componentă web, aceasta va fi executată (primind cererea) şi rezultatul acestei operaţii integrat în răspuns. Resursa web inclusă va avea acces la obiectul cerere, însă va fi limitată în privinţa obiectului răspuns în sensul că poate modifica conţinutul acestuia dar nu şi antetele sale (ca atare, nu poate apela metodele care modifică antetele obiectului răspuns).
Pentru transmiterea mai departe a cererii către o altă resursă pentru a realiza alte procesări şi a genera răspunsul, se foloseşte metoda [[https://docs.oracle.com/javaee/7/api/javax/servlet/RequestDispatcher.html#forward-javax.servlet.ServletRequest-javax.servlet.ServletResponse-|forward()]] a obiectului ''RequestDispatcher''. URL-ul cererii va fi modificat la cel al paginii care va realiza prelucrarea ei. De asemenea, se reţine şi URI-ul original şi parţile sale componente ca atribute ale cererii. Responsabilitatea răspunsului îi revine resursei către care a fost transmis răspunsul. O astfel de funcţionalitate este însă limitată în cazul în care au fost folosite obiecte ''ServletOutputStream'' sau ''PrintWriter'' în cadrul servletului, generându-se în caz contrar excepţii de tipul ''IllegalStateException''.
Transmiterea controlului către obiectele Java Servlet asociate tipurilor de utilizatori autentificate în sistem se poate realiza astfel:
RequestDispatcher dispatcher = null;
switch(getUserType(username, password)) {
case Constants.USER_TYPE_ADMINISTRATOR:
dispatcher = getServletContext().getRequestDispatcher("/" + Constants.ADMINISTRATOR_SERVLET_CONTEXT);
break;
case Constants.USER_TYPE_CLIENT:
dispatcher = getServletContext().getRequestDispatcher("/" + Constants.CLIENT_SERVLET_CONTEXT);
break;
}
if (dispatcher != null) {
dispatcher.forward(request,response);
}
Accesarea contextului web în care sunt executate componentele se face prin metoda ''getServletContext()'' care întoarce un obiect de tipul [[http://docs.oracle.com/javaee/7/api/javax/servlet/ServletContext.html|ServletContext]], prin intermediul căruia pot fi accesaţi parametrii de iniţializare, resurse asociate cu contextul web, atribute având tipuri de obiecte asociate şi capabilităţi legate de jurnalizare.
==== Încărcarea de fișiere ====
Una dintre funcţionalităţile cele mai frecvent implementate în cazul unei aplicaţii web o reprezintă încărcarea de fişiere. În acest scop, adnotarea [[http://docs.oracle.com/javaee/7/api/javax/servlet/annotation/MultipartConfig.html|@MultipartConfig]] este folosită pentru a indica faptul că servletul pentru care este declarată poate prelucra cereri folosind tipul MIME ''multipart/form-date'', componentele [[http://docs.oracle.com/javaee/7/api/javax/servlet/http/Part.html|javax.servlet.Part]] putând fi obţinute prin una din metodele [[http://docs.oracle.com/javaee/7/api/javax/servlet/http/HttpServletRequest.html#getPart-java.lang.String-|request.getPart(String name)]] sau [[http://docs.oracle.com/javaee/7/api/javax/servlet/http/HttpServletRequest.html#getParts--|request.getParts()]].
Adnotarea ''@MultipartConfig'' suportă mai multe atribute opţionale:
* [[http://docs.oracle.com/javaee/7/api/javax/servlet/annotation/MultipartConfig.html#location--|location]] – reprezintă calea absolută spre un director din sistemul de fişiere; nu sunt suportate căi relative la contextul aplicaţiei web; de regulă, locaţia este folosită pentru a stoca fişiere temporare în timp ce părţile fişierului sunt procesate sau atunci când dimensiunea fişierului depăşeşte valoarea sprecificată de proprietatea ''fieldSizeThreshold''; valoarea implicită este "";
* [[http://docs.oracle.com/javaee/7/api/javax/servlet/annotation/MultipartConfig.html#fileSizeThreshold--|fieldSizeThreshold]] – dimensiunea fişierului (în octeţi) după care acesta va fi stocat temporar pe disc; valoarea implicită este 0 octeţi;
* [[http://docs.oracle.com/javaee/7/api/javax/servlet/annotation/MultipartConfig.html#maxFileSize--|maxFileSize]] – dimensiunea maximă permisă pentru încărcarea de fişiere, exprimată în octeţi; în situaţia în care se încearcă încărcarea de fişiere care depăşesc această valoare, se generează o excepţie de tipul ''IllegalStateException''; valoarea implicită este nelimitată;
* [[http://docs.oracle.com/javaee/7/api/javax/servlet/annotation/MultipartConfig.html#maxRequestSize--|maxRequestSize]] – dimensiunea maximă pentru o cerere ''multipart/form-date'', exprimată în octeţi; containerul va genera o excepţie dacă valoarea totală a tuturor fişierelor încărcate depăşeşte acest prag; valoarea implicită este de asemenea nelimitată.
Alternativ, aceste proprietăţi pot fi specificate în fişierul de configurare ''web.xml'', în cadrul secţiunii '' ... '', denumirile câmpurilor fiind identice cu cele ale proprietăţilor adnotării.
Specificaţia Java Servlet defineşte pentru un obiect ''HttpServletRequest'':
* ''Collection getParts()'' – în cazul în care sunt încărcate fişiere având tipuri diferite; poate fi folosit un obiect ''Iterator'' spre a fi parcurse părţile;
* ''Part getPart(String name)'' – poate fi folosit pentru a obţine o parte identificată printr-un nume.
Interfaţa ''javax.servlet.http.Part'' oferă metode pentru analiza fiecărei părţi cu privire la nume, dimensiune, tipul de conţinut, procesarea antetelor transmise împreună cu partea respectivă, salvarea pe disc (prin intermediul metodei [[https://docs.oracle.com/javaee/7/api/javax/servlet/http/Part.html#write-java.lang.String-|write(String filename)]]) sau ştergerea ei.
==== Procesarea asincronă ====
Containerele asociază câte un fir de execuţie pentru fiecare cerere provenită de la client. În condiţii de încărcare foarte mare, este necesar un număr corespunzător de fire de execuţie, situaţie în care se poate ajunge la depăşirea memoriei disponibile sau a numărului de fire de execuţie. Scalabilitatea aplicaţiilor se obţine asigurându-se faptul că nici un fir de execuţie nu rămâne nefolosit (cu alte cuvinte, că nu sunt blocate în aşteptarea unor resurse), astfel încât să poată fi utilizat pentru alte cereri. În aceste cazuri **procesarea asincronă** presupune crearea unui fir de execuţie pentru fiecare operaţie blocantă şi eliberarea firelor de execuţie ocupate.
Dacă în procesul de tratare a unei cereri un servlet întâlneşte o operaţie care ar putea fi blocantă, aceasta poate fi delegată către un context de execuţie asincron cu eliberarea firului de execuţie ocupat către container, fără a se genera un răspuns. În momentul în care operaţia în cauză se termină, va putea fi generat şi răspunsul aferent cererii fie din contextul de execuţie asincron, fie din contextul altui servlet către care este transmis mai departe.
Pentru a permite procesarea asincronă a unui servlet, trebuie specificată proprietatea [[http://docs.oracle.com/javaee/7/api/javax/servlet/annotation/WebServlet.html#asyncSupported--|asyncSupported]] avand valoarea ''true'' în cadrul adnotării ''@WebServlet''. Funcţionalitatea pentru a realiza aceste operaţii este oferită de clasa [[https://docs.oracle.com/javaee/7/api/javax/servlet/AsyncContext.html|javax.servlet.AsyncContext]], un obiect de acest tip putând fi obţinut în metoda ''service()'' din obiectul cerere:
public void service(HttpServletRequest request, HttpServletResponse response) {
// ...
AsyncContext asyncContext = request.startAsync();
// ...
}
Astfel, cererea este transferată într-un context asincron, astfel încât răspunsul nu va fi transmis la ieşirea din metoda ''service()''. El va trebui generat în contextul asincron după terminarea operaţiei blocante sau se transmite cererea mai departe către un alt servlet.
Metodele definite de clasa ''AsyncContext'' sunt:
* [[https://docs.oracle.com/javaee/7/api/javax/servlet/AsyncContext.html#start-java.lang.Runnable-|void start(Runnable run)]] – containerul oferă un alt fir de execuţie unde operaţia blocantă poate fi procesată; codul care tratează această prelucrare trebuie specificat în clasa care implementează interfaţa ''Runnable''; o asfel de clasă poate fi definită ca internă atunci când se apelează metoda ''start()'' sau se poate folosi alt mecanism pentru a transmite clasei instanţa ''AsyncContext'';
* [[https://docs.oracle.com/javaee/7/api/javax/servlet/AsyncContext.html#getRequest--|ServletRequest getRequest()]] – întoarce cererea folosită pentru a iniţializa contextul asincron; metoda poate fi folosită spre a obţine parametrii cererii în cadrul contextului asincron;
* [[https://docs.oracle.com/javaee/7/api/javax/servlet/AsyncContext.html#getResponse--|ServletResponse getResponse()]] – întoarce răspunsul utilizat spre a iniţializa contextul asincron; metoda poate fi folosită pentru a construi un răspuns cu rezultatele operaţiei blocante;
* [[https://docs.oracle.com/javaee/7/api/javax/servlet/AsyncContext.html#complete--|void complete()]] – termină operaţia asincronă şi transmite răspunsul asociat cu contextul asincron; poate fi apelată după constuirea răspunsului;
* [[https://docs.oracle.com/javaee/7/api/javax/servlet/AsyncContext.html#dispatch-java.lang.String-|void dispatch(String path)]] – transmite obiectele cerere şi răspuns către calea indicată; metoda este folosită pentru a delega altui servlet responsabilitatea cu privire la aceste obiecte, după terminarea operaţiei.
Un exemplu de utilizare al acestei funcţionalităţi ar putea fi:
@WebServlet(urlPatterns={"/AsyncServlet"}, asyncSupported=true)
public class AsyncServlet extends HttpServlet {
@Override
public void service(HttpServletRequest request, HttpServletResponse response) {
response.setContentType("text/html");
final AsyncContext asyncContext = request.startAsync();
asyncContext.start(new Runnable() {
public void run() {
HttpServletRequest request = asyncContext.getRequest();
String value = request.getParameter(attribute);
HttpServletResponse response = asyncContext.getResponse();
response.getWriter().println(blockingOperation(value));
asyncContext.complete();
}
});
}
}
Prin proprietatea ''asyncSupported=true'' se specifică faptul că servletul are capacitatea de a realiza procesare asincronă. Metoda ''request.startAsync()'' determină procesarea asincronă a cererii, astfel încât răspunsul să nu fie transmis către client la sfârşitul metodei ''service()''. Metoda ''startAsync()'' se întoarce imediat, iar cererea este procesată în cadrul contextului asincron. Apelul ''asyncContext.start(...)'' creează un nou fir de execuţie în container. Codul metodei ''run()'' a clasei interne se execută în noul fir de execuţie, însă are acces la obiectele cerere şi răspuns ale contextului din care a fost apelată. Metoda ''complete()'' transmite răspunsul către client.
==== Operații de intrare/ieșire non-blocante ====
În situaţia în care operaţiile de intrare/ieşire se desfăşoară mai rapid pe server decât pe client (datorită limitărilor introduse de comunicaţia prin reţeaua de calculatoare), se pot folosi **operaţii de intrare/ieşire non-blocante** asociate cu procesarea asincronă pentru a se asigura eliminarea timpilor morţi din firele de execuţie. Astfel, fluxurilor de intrare/ieşire li se vor asocia mai multe obiecte ascultător, iar operaţiile de citire/scriere se vor realiza în metodele care tratează aceste evenimente.
Clasa [[http://docs.oracle.com/javaee/7/api/javax/servlet/ServletInputStream.html|javax.servlet.ServletInputStream]] suportă metoda [[http://docs.oracle.com/javaee/7/api/javax/servlet/ServletInputStream.html#setReadListener-javax.servlet.ReadListener-|void setReadListener(ReadListener rl)]] care asociază un obiect ascultător fluxului de intrare care conţine metode pentru a citi date asincron. Acesta este creat ca o clasă internă sau se foloseşte alt mecanism pentru a transmite fluxul de intrare obiectului ascultător.
Metodele de tratare a evenimentelor asociate unui obiect [[http://docs.oracle.com/javaee/7/api/javax/servlet/ReadListener.html|ReadListener]] sunt [[http://docs.oracle.com/javaee/7/api/javax/servlet/ReadListener.html#onDataAvailable--|void onDataAvailable()]], [[http://docs.oracle.com/javaee/7/api/javax/servlet/ReadListener.html#onAllDataRead--|void onAllDataRead()]], [[http://docs.oracle.com/javaee/7/api/javax/servlet/ReadListener.html#onError-java.lang.Throwable-|void onError(Throwable t)]].
* [[http://docs.oracle.com/javaee/7/api/javax/servlet/ServletInputStream.html#isReady--|boolean isReady()]] – întoarce ''true'' dacă datele pot fi citite non-blocant;
* [[http://docs.oracle.com/javaee/7/api/javax/servlet/ServletInputStream.html#isFinished--|boolean isFinished()]] – întoarce ''true'' când toate datele au fost citite.
Clasa [[http://docs.oracle.com/javaee/7/api/javax/servlet/ServletOutputStream.html|javax.servlet.ServletOutputStream]] suportă metodele:
* [[http://docs.oracle.com/javaee/7/api/javax/servlet/ServletOutputStream.html#setWriteListener-javax.servlet.WriteListener-|void setWriteListener(WriteListener wl)]] – asociază un obiect ascultător fluxului de ieşire care conţine metode pentru a scrie date asincron; acesta este creat ca o clasă internă sau se foloseşte alt mecanism pentru a transmite fluxul de intrare obiectului ascultător;
* [[http://docs.oracle.com/javaee/7/api/javax/servlet/ServletOutputStream.html#isReady--|boolean isReady()]] – întoarce ''true'' dacă datele pot fi scrise non-blocant.
Metodele de tratare a evenimentelor asociate unui obiect [[http://docs.oracle.com/javaee/7/api/javax/servlet/WriteListener.html|WriteListener]] sunt [[http://docs.oracle.com/javaee/7/api/javax/servlet/WriteListener.html#onWritePossible--|void onWritePossible()]], [[http://docs.oracle.com/javaee/7/api/javax/servlet/WriteListener.html#onError-java.lang.Throwable-|void onError(Throwable t)]].
==== Integrarea Java Servlets cu sisteme de gestiune pentru baze de date ====
Ca în orice aplicaţie Java, accesul la baza de date se face folosind metodele puse la dispoziţie prin API-ul JDBC (pachetul ''java.sql''), biblioteca ce conţine "driver"-ul de conectare.
Modificările care se impun în cazul unei aplicaţii Internet sunt nesemnificative, clasele dezvoltate pentru subnivelul de acces la date (utilizate pentru aplicaţii desktop) putând fi refolosite în acest caz spre a genera conţinutul dinamic obţinut din baza de date, fără a fi necesară instalarea de utilitare pe client, informaţiile fiind vizualizate prin intermediul browser-ului.
Încărcarea "driver"-ului de conectare trebuie să se realizeze explicit, prin intermediul metodei ''Class.forName("com.mysql.jdbc.Driver");'' înainte de apelarea oricărei funcționalități oferite de API-ul JDBC întrucât această operație nu este realizată de serverul Apache Tomcat. De asemenea, este invocată în mod automat metoda ''DriverManager.registerDriver()'' primind ca parametru instanța obținută, astfel încât acesta să fie inclus în lista de "drivere" disponibile pentru obținerea de conexiuni.
try {
Class.forName("com.mysql.jdbc.Driver");
} catch(ClassNotFoundException exception) {
System.out.println("An exception has occurred: " + exception.getMessage());
if(Constants.DEBUG) {
exception.printStackTrace();
}
}
==== Mecanisme pentru gestiunea stării în Java Servlets ====
Protocolul HTTP este un protocol fără stare fiind caracterizat prin cereri şi răspunsuri ca tranzacţii izolate.
În comparație cu acesta, protocolul FTP care este un protocol cu stări (clientul se conectează la server, realizează operaţiile pe baza conexiunii după care se realizează deconectarea).
Problema apare în momentul când trebuie să se coreleze mai multe accesări (care provin de la acelaşi utilizator).
Soluţiile pentru rezolvarea acestui inconvenient implică:
- utilizarea de câmpuri ascunse;
- rescrierea URL-urilor pentru a include parametrii suplimentari;
- utilizarea de cookie-uri;
- folosirea unor instrumente de "urmărire" a sesiunii.
=== Utilizarea de câmpuri ascunse ===
**Câmpurile ascunse** pot fi conţinute în formularele din paginile HTML (elemente de tip ''''), dar au dezavantajul că pot fi identificate cu uşurinţă prin inspectarea codului sursă.
=== Rescrierea URL-urilor ===
**Rescrierea URL**-urilor presupune adăugarea unei (aceleiaşi) informaţii la URL-urile paginilor care sunt transmise utilizatorului, informaţia fiind primită automat de server pentru cererile din pagina respectivă. Această metodă se foloseşte împreună cu protocolul ''HTTP GET''.
==== ====
Ambele soluţii presupun generarea dinamică a paginilor ca şi includerea unui formular.
=== Utilizarea de cookie-uri ===
**Cookie-urile** sunt fişiere care conţin perechi de tipul (cheie, valoare), fiind create de server şi incluse ca instrucţiuni în antetul mesajului HTTP transmis ca răspuns.
În pachetul ''javax.servlet.http'' este definită clasa [[http://docs.oracle.com/javaee/7/api/javax/servlet/http/Cookie.html|Cookie]]:
public class Cookie extends java.lang.Object implements java.lang.Cloneable, java.io.Serializable
având un constructor care primeşte două şiruri de caractere reprezentând cheia, respectiv valoarea.
Pentru un obiect de tip ''Cookie'' se pot gestiona denumirea ([[http://docs.oracle.com/javaee/7/api/javax/servlet/http/Cookie.html#getName--|getName()]]), valoarea ([[http://docs.oracle.com/javaee/7/api/javax/servlet/http/Cookie.html#getValue--|getValue()]], [[http://docs.oracle.com/javaee/7/api/javax/servlet/http/Cookie.html#setValue-java.lang.String-|setValue()]]), timpul de expirare ([[http://docs.oracle.com/javaee/7/api/javax/servlet/http/Cookie.html#getMaxAge--|getMaxAge()]], [[http://docs.oracle.com/javaee/7/api/javax/servlet/http/Cookie.html#setMaxAge-int-|setMaxAge()]]), domeniul ([[http://docs.oracle.com/javaee/7/api/javax/servlet/http/Cookie.html#getDomain--|getDomain()]], [[http://docs.oracle.com/javaee/7/api/javax/servlet/http/Cookie.html#setDomain-java.lang.String-|setDomain()]]) sau calea ([[http://docs.oracle.com/javaee/7/api/javax/servlet/http/Cookie.html#getPath--|getPath()]], [[http://docs.oracle.com/javaee/7/api/javax/servlet/http/Cookie.html#setPath-java.lang.String-|setPath()]]).
Obiectul cookie poate fi inclus într-un obiect de tip ''HttpServletResponse'', prin intermediul metodei [[http://docs.oracle.com/javaee/7/api/javax/servlet/http/HttpServletResponse.html#addCookie-javax.servlet.http.Cookie-|addCookie()]]:
response.addCookie(cookie);
Cookie-urile pot fi obţinute prin metoda [[http://docs.oracle.com/javaee/7/api/javax/servlet/http/HttpServletRequest.html#getCookies--|getCookies()]] implementată în clasa ''HttpServletRequest''.
Cookie[] cookies = request.getCookies();
=== Folosirea unor instrumente de "urmărire" a sesiunii ===
În pachetul ''javax.servlet.http'' este definită şi interfaţa [[https://docs.oracle.com/javaee/7/api/javax/servlet/http/HttpSession.html|HttpSession]], care crează un singur obiect pentru o **sesiune**, putând stabili anumite valori pentru identificarea conexiunii dintre client şi server, legătura fiind realizată prin cookie (dacă sunt acceptate de client) sau rescrierea URL-urilor.
HttpSession session = request.getSession(true);
Metoda [[http://docs.oracle.com/javaee/7/api/javax/servlet/http/HttpServletRequest.html#getSession--|getSession()]] întoarce sesiunea asociată unei cereri, sau dacă nu există o sesiune asociată cererii, aceasta este creată, dacă nu se specifică astfel. Metoda ''getSession()'' poate fi apelată şi fără parametri: ''getSession() = getSession(true)''.
Atribute ce pot reţine diferite obiecte sunt asociate unei sesiuni prin intermediul unei denumiri. Acestea sunt accesibile oricărei componente web care aparţine contextului respectiv şi tratează cereri care fac parte din aceeaşi sesiune. Ele pot fi notificate cu privire la producerea evenimentelor legate de asocierea lor cu o sesiune sau chiar de sesiunea in cauză. Astfel, clasele ascultător vor implementa interfaţa [[https://docs.oracle.com/javaee/7/api/javax/servlet/http/HttpSessionBindingListener.html|javax.servlet.http.HttpSessionBindingListenerInterface]] pentru a monitoriza asocierea sau disocierea unui obiect cu o sesiune, respectiv interfaţa [[https://docs.oracle.com/javaee/7/api/javax/servlet/http/HttpSessionActivationListener.html|javax.servlet.http.HttpSessionActivationListener]] spre a se detecta momentul când sesiunea este activată sau pasivizată (la transferul între maşini virtuale, respectiv la încărcarea / descărcarea dintr-un depozit persistent).
Obţinerea atributelor este realizată prin metodele [[http://docs.oracle.com/javaee/7/api/javax/servlet/ServletRequest.html#getAttributeNames--|getAttributeNames()]], respectiv [[http://docs.oracle.com/javaee/7/api/javax/servlet/ServletRequest.html#getAttribute-java.lang.String-|getAttribute(String)]], iar stabilirea lor prin metoda [[http://docs.oracle.com/javaee/7/api/javax/servlet/ServletRequest.html#setAttribute-java.lang.String-java.lang.Object-|setAttribute(String, Object)]]. Totodată, un atribut poate fi eliminat prin metoda [[http://docs.oracle.com/javaee/7/api/javax/servlet/ServletRequest.html#removeAttribute-java.lang.String-|removeAttribute(String)]]. Alternativ, un atribut poate fi reiniţializat (având conţinut vid) şi retransmis sub această formă prin aceasta reţinându-se o anumită stare a aplicaţiei.
Întrucât un client HTTP nu poate specifica momentul în care sesiunea nu mai este necesară, este asociat un timp de expirare (prin metodele [[https://docs.oracle.com/javaee/7/api/javax/servlet/http/HttpSession.html#getMaxInactiveInterval--|getMaxInactiveInterval()]], [[https://docs.oracle.com/javaee/7/api/javax/servlet/http/HttpSession.html#setMaxInactiveInterval-int-|setMaxInactiveInterval()]]), astfel încât resursele utilizate de sesiune să poată fi refolosite. O sesiune nu va expira în cazul în care este accesată periodic (frecvenţa fiind mai mare decât timpul de expirare) – prin metodele serviciu care iniţializează (din nou) perioada în care sesiunea poate fi utilizată. Totodată, atunci când interacţiunea cu un client HTTP este încheiată, se poate folosi metoda [[https://docs.oracle.com/javaee/7/api/javax/servlet/http/HttpSession.html#invalidate--|invalidate()]] pentru a distruge sesiunea eliberând resursele folosite.
Asocierea unei sesiuni cu un utilizator implică de regulă transmiterea unui identificator între client şi server. Acesta poate fi reţinut pe client sub formă de cookie sau poate fi inclus în fiecare URL care este transmis clientului. Urmărirea sesiunii presupune faptul că aplicaţia ar trebui să rescrie URL-ul (folosind metoda [[http://docs.oracle.com/javaee/7/api/javax/servlet/http/HttpServletResponse.html#encodeURL-java.lang.String-|encodeURL(String)]] pe obiectul răspuns) de fiecare dată când servlet-ul transmite un răspuns, astfel încât, în situaţia în care clientul dezactivează posibilitatea de a reţine cookie-uri, identificatorul să fie inclus în URL. În situaţia în care clientul acceptă cookie-uri, efectul metodei ''encodeURL(URL)'' este nul, lăsând URL-ul pe care îl primeşte ca parametru neschimbat.
===== Activitate de Laborator =====
==== Structura aplicației web BookStore ====
Se doreşte implementarea unei interfeţe grafice cu utilizatorul (dezvoltată sub forma unei aplicaţii web, folosind serverul Apache Tomcat 8.x) pentru gestiunea informaţiilor reţinute într-o bază de date, aceasta urmând a fi utilizată de un sistem ERP destinat unei librării care comercializează doar cărţi.
Sistemul informatic va putea fi accesat de:
* un utilizator tip **administrator**, care va manipula – prin intermediul aplicaţiei – informaţiile din baza de date; acesta are la dispoziție operații de adăugare, editare și ștergere pentru oricare dintre tabele;
* un utilizator tip **client**, care va formula o comandă după ce şi-a specificat un anumit coş de cumpărături ca urmare a consultării catalogului de produse; pe baza comenzii va fi emisă o factură (''invoice_header''), caracterizată printr-un număr de identificare (''identification_number''), data la care a fost emisă (''issue_date''), stare (''state'') și identificatorul utilizatorului către care va fi transmisă (''user_id''); aceasta conține la rândul ei mai multe înregistrări, corespunzătoare cărților care au fost cumpărate (''invoice_line''), definite de factura corespunzătoare (''invoice_header_id''), identificatorul formatului de prezentare al cărții (''book_presentation_id'') și cantitatea (''quantity'').
Pentru fiecare funcționalitate există o pagină dedicată, gestionată de câte o clasă Java Servlet: pagina de autentificare (''LoginServlet'') de unde, în funcţie de rolul utilizatorului se trece la pagina de tip administrator (''AdministratorServlet'') respectiv pagina de tip client (''ClientServlet'').
{{ :laboratoare:laborator04:bookstore_webapplication_structure.png?nolink&300 }}
**Pagina de tip client din cadrul aplicaţiei BookStore**
===== =====
**0**. Să se cloneze în directorul de pe discul local conținutul depozitului la distanță de la [[https://www.github.com/aipi2015/Laborator04|]]. În urma acestei operații, directorul ''Laborator04'' va trebui să conțină subdirectorul ''labtasks'', fișierele ''README.md'' și ''LICENSE''.
student@aipi2015:~$ git clone https://www.github.com/aipi2015/Laborator04.git
**1**. Să se ruleze, folosind MySQL Workbench (sau alt utilitar similar), scriptul ''Laborator04l.sql'', localizat în directorul ''scripts''. Acesta instalează baza de date ''bookstore''.
În cazul în care baza de date ''bookstore'' este deja instalată, nu mai este necesar să se ruleze acest script.Această operație poate dura o perioadă de timp îndelungată, în funcție de configurațiile mașinilor pe care se lucrează.
**2.** În cazul Unix, daţi drepturi de execuţie tuturor fişierelor .sh din directorul ''bin'' al serverului Apache Tomcat, întrucât acestea sunt apelate la rândul lor de ''startup.sh'', respectiv ''shutdown.sh''.
student@aipi2015:~/apache-tomcat-8.0.28$ cd bin
student@aipi2015:~/apache-tomcat-8.0.28/bin$ sudo chmod +x *.sh
**3.** Modificați în interfața ''Constants'' din pachetul ''ro.pub.cs.aipi.lab04.general'' informațiile necesare obținerii drepturilor de acces la baza de date (nume de utilizator, parola).
**4.** Să se configureze mediile integrate de dezvoltare astfel încât acestea să fie interfațate cu serverul web Apache Tomcat 8.x. Mai multe informații cu privire la modul în care poate fi realizată interfațarea dintre aceste componente pot fi găsite la [[:utile:apache_tomcat|serverul Apache Tomcat]].
==== Eclipse ====
Să se creeze o referinţă către serverul HTTP Apache Tomcat care să fie adăugată la proiectul 04-BookStore-JavaServlets.
Să se deschidă proiectul folosind opţiunea //File// → //Import// → //General// → //Existing Project into Workspace..//.
Se accesează opţiunea //Run as...// → //Run On Server// din meniul contextual al aplicaţiei 04-BookStore-JavaServlets (right-click), bifându-se opţiunea //Always use this server when running this project//.
{{ :laboratoare:laborator04:eclipse01.png?nolink }}
{{ :laboratoare:laborator04:eclipse02.png?nolink }}
{{ :laboratoare:laborator04:eclipse03.png?nolink }}
==== NetBeans ====
Să se creeze o referinţă către serverul HTTP Apache Tomcat care să fie adăugată la proiectul 04-BookStore-JavaServlets.
Se accesează opţiunea //Deploy// din meniul contextual al proiectului (accesat cu right-click).
{{ :laboratoare:laborator04:netbeans01.png?nolink }}
Alternativ, se poate porni serverul Apache Tomcat și dezvolta aplicația web în contextul său și prin opțiunea //Run// (F6), care va lansa și browser-ul implicit în care va fi prezentată pagina principală a aplicației web.
===== =====
**5.** Să se testeze aplicaţia prin accesarea adresei [[http://localhost:8080/04-BookStore-JavaServlets/|]].
Se poate consulta script-ul ''Laborator04l.sql'' există exemple de utilizatori care pot fi folosite pentru accesarea paginilor de administrator, respectiv de client.
* administrator: mary.smith / -
* client: linda.williams / -
**6.** Să se acceseze pagina de tip administrator şi să se testeze funcţionalităţile implementate în cadrul acesteia (adăugare, editare, ştergere).
**7.** Să se acceseze pagina de tip client. Să se implementeze funcționalitatea corespunzătoare filtrării în funcție de limbile în care sunt disponibile cărțile din librărie. Un utilizator poate adăuga o valoare din lista de limbi disponibile. Filtrarea se face în funcție de limbile selectate, pentru acele formate de prezentare a cărții care conțin cel puțin una dintre aceste valori. În situația în care nu s-a specificat nici o valoare, sunt incluse toate volumele comercializate. Un utilizator poate șterge una sau mai multe dintre limbile selectate anterior.
{{ :laboratoare:laborator04:activitate_de_laborator_exercitiu07.png?nolink }}
În clasa ''ClientGraphicUserInterface'' din pachetul ''ro.pub.cs.aipi.lab04.graphicuserinterface'', se afișează lista cu limbile în funcție de care se realizează filtrarea, pentru fiecare dintre acestea atașându-se un buton pentru eliminarea valorii respective.
Se iterează pe lista cu filtre în funcție de limbi (obiectul ''languagesFilter''), iar pentru fiecare element:
* se afișează șirul de caractere conținând limba;
* se afișează un element de tip [[http://www.w3schools.com/tags/tag_input.asp|]], având atributul ''type'' cu valoarea ''image'' și denumirea ''delete_language: _{language_name}'' (valori reținutr de constantele ''DELETE_BUTTON_NAME'' și ''LANGUAGE'' din interfața ''Constants''). Resursa grafică se regăsește în directorul ''images'', în subdirectorul ''user_interface'', având denumirea ''delete.png'' (se recomandă să se forțeze dimensiunea acesteia la 16x16, prin specificarea atributelor ''width'' și ''height'').
Elementele vor fi incluse ca linii în cadrul unui tabel (''
...
'').
În clasa ''ClientServlet'' din pachetul ''ro.pub.cs.aipi.lab04.servlets'', se tratează acțiunile corespunzătoare evenimentelor de tip apăsare a butoanelor de tip ''insert'', respectiv ''delete'' corespunzătoare filtrului în funcție de limba în care a fost selectată cartea.
* pentru parametrii având denumirea de tipul ''insert_language: '' (valori conținute de constantele ''INSERT_BUTTON_NAME'', respectiv ''LANGUAGE'' din interfața ''Constants''), se obține denumirea limbii (ca valoare atașată parametrului, prin invocarea metodei ''request.getParameter(parameter)''), se verifică dacă aceasta nu este conținută în lista de limbi în funcție de care se face filtrarea și, în caz afirmativ, este adăugată la listă, marcându-se faptul că lista de filtre a fost modificată (parametrul ''filterChange'' de tip ''boolean'');
* pentru parametrii având denumirea de tipul ''delete_language: _{language_name}'' (valori conținute de constantele ''DELETE_BUTTON_NAME'', respectiv ''LANGUAGE'' din interfața ''Constants''), se obține denumirea limbii (ca valoare atașată parametrului, prin invocarea metodei ''request.getParameter(parameter)''), se verifică dacă aceasta este conținută în lista de limbi în funcție de care se face filtrarea și, în caz afirmativ, este ștearsă din listă, marcându-se faptul că lista de filtre a fost modificată (parametrul ''filterChange'' de tip ''boolean'').
Se poate utiliza ca model implementarea corespunzătoare filtrării în funcție de formatul de prezentare a cărții.
**8.** În pachetul ''ro.pub.cs.aipi.lab04.servlets'', în clasa ''ClientServlet'' să se completeze metoda ''doPost()'' pentru a crea conţinutul coşului de cumpărături, ţinând cont de situaţia în care pentru un produs deja existent să se poate actualiza cantitatea, astfel: dacă un produs există deja în coşul de cumpărături, cantitatea respectivă va fi suprascrisă, iar dacă aceasta este 0, produsul va fi şters din coşul de cumpărături.
În cazul în care se apasă un buton atașat unui câmp text pentru precizarea unei cantități pentru un anumit format de prezentare a unei cărți, este necesar să se realizeze următoarele operații:
* se identifică identificatorul formatului de prezentare a cărții și cantitatea corespunzătoare:
{{ :laboratoare:laborator04:activitate_de_laborator_exercitiu08.png?nolink&700 }}
**a)** **butonul** are denumirea ''insert_shoppingcart_book_presentation_id'' unde ''book_presentation_id'' reprezintă identificatorul formatului de prezentare a cărții (de exemplu, ''insert_shoppingcart_1'' pentru formatul de identificare a cărții cu identificatorul 1); prin parsarea denumirii acestui parametru se obține identificatorul formatului de prezentare a cărții:
if (parameter.startsWith(Constants.INSERT_BUTTON_NAME.toLowerCase() + "_" + Utilities.removeSpaces(Constants.SHOPPING_CART.toLowerCase()) + "_") && parameter.endsWith(".x")) {
String bookPresentationId = parameter.substring(parameter.lastIndexOf("_") + 1, parameter.indexOf(".x"));
}
Pentru un element de tip '''' având atributul ''type'' cu valoarea ''submit'', se transmit ca parametrii coordonatele la care se găsește mouse-ul în momentul în care se produce evenimentul de tip apăsare, acestea având aceeași denumire, sufixată de ''.x'', respectiv ''.y''. Este important ca un singur parametru să fie procesat, în acest caz.
**b)** **câmpul text** ce conține cantitatea corespunzătoare are denumirea ''copies_shoppingcart_book_presentation_id'' unde ''book_presentation_id'' reprezintă identificatorul formatului de prezentare a cărții (de exemplu, ''copies_shoppingcart_1'' pentru formatul de identificare a cărții cu identificatorul 1); valoarea corespunzătoare acestui parametru reprezintă cantitatea solicitată:
if (parameter.startsWith(Constants.INSERT_BUTTON_NAME.toLowerCase() + "_" + Utilities.removeSpaces(Constants.SHOPPING_CART.toLowerCase()) + "_") && parameter.endsWith(".x")) {
String quantity = request.getParameter(Constants.COPIES.toLowerCase() + "_" + Utilities.removeSpaces(Constants.SHOPPING_CART.toLowerCase()) + "_" + bookPresentationId);
}
Vor fi transmiși un număr de parametrii egal cu numărul de câmpuri text afișate în cadrul paginii Internet, însă va fi luat în considerare doar cel asociat butonului care a fost apăsat, chiar dacă pot fi introduse valori și în cadrul acestora. Obținerea valorii transmise prin intermediul unui parametru se realizează prin apelul ''request.getParameter(parameter)''.
* se iterează asupra conținutului coșului de cumpărături (obiectul ''shoppingCart'', transmis prin intermediul sesiunii, este de tipul ''List'' unde ca atribut se reţine identificatorul formatului de prezentare a cărţii, iar ca valoare cantitatea):
* dacă se identifică un element având același identificator al formatului de prezentare a cărții:
* în situația în care cantitatea este nenulă, aceasta este modificată;
* în situația în care cantitatea este nulă, aceasta este ștearsă;
* dacă nu se identifică un element având același identificator al formatului de prezentare a cărții, acesta este adăugată.
**9.** În pachetul ''ro.pub.cs.aipi.lab04.graphicuserinterface'', în clasa ''ClientGraphicUserInterface'', să se completeze metoda ''displayClientGraphicUserInterface()'' astfel încât să se vizualizeze coţinutul coşului de cumpărături. Se va afişa numărul de exemplare comandate, titlul cărţii, formatul de prezentare și limba, precum şi suma pentru fiecare produs în parte, respectiv preţul total pentru întregul coş de cumpărături.
{{ :laboratoare:laborator04:activitate_de_laborator_exercitiu09.png?nolink }}
Se iterează asupra conținutului coșului de cumpărături (obiectul ''shoppingCart''), determinându-se, pentru fiecare element, identificatorul formatului de prezentare (atributul) și cantitatea (valoarea).
Prețul unitar pentru un format de prezentare poate fi obținut apelând metoda ''getPrice()'' din clasa ''BookPresentationManager'', ce primește ca argument identificatorul formatului de prezentare respectiv.
Informațiile cu privire la un format de prezentare (titlul, formatul de prezentare și limba) pot fi obținute apelând metoda ''getInformation()'' din clasa ''BookPresentationManager'', ce primește ca argument identificatorul formatului de prezentare respectiv. Acesta întoarce o listă de obiecte de tip șir de caractere.
Este necesar să se calculeze și prețul total al coșului de cumpărături.
Prezentarea se va face în cadrul unui tabel (element de tip [[http://www.w3schools.com/tags/tag_table.asp|
]]), datele corespunzătoare unui obiect din coșul de cumpărături fiind incluse pe o linie a acestuia (''
...
'').
**10.** În pachetul ''ro.pub.cs.aipi.lab04.graphicuserinterface'', în clasa ''ClientGraphicUserInterface'' să se completeze metoda ''displayClientGraphicUserInterface()'' astfel încât să se adauge două butoane prin care se poate anula, respectiv finaliza o comandă, în condițiile în care există produse în coșul de cumpărături.
Resursele grafice pentru butoane se regăsesc în directorul ''images'', în subdirectorul ''user_interface'', având denumirile:
* ''remove_from_shopping_cart.png'';
* ''shopping_cart_accept.png'';
Butoanele vor fi incluse în cadrul unui element de tip [[http://www.w3schools.com/tags/tag_input.asp|]], având atributul ''type'' cu valoarea ''image'' și denumirile ''cancelcommand'', respectiv ''completecommand'' (valori conținute de constantele ''CANCEL_COMMAND'' și ''COMPLETE_COMMAND'' din interfața ''Constants'', pentru care se elimină spațiile și se folosesc numai minuscule). Locațiile la care se găsesc resursele ce vor fi afișate vor fi precizate în cadrul atributului ''src''.
**11.** În pachetul ''ro.pub.cs.aipi.lab04.servlets'', în clasa ''ClientServlet'', metoda ''doPost()'', să se implementeze operaţiile de anulare, respectiv finalizare a unei comenzi.
În cazul anulării unei comenzi, se şterge conţinutul obiectului ''shoppingCart''.
În cazul finalizării unei comenzi, trebuie realizate următoarele operaţii:
* se adaugă o înregistrare în tabela ''invoice_header''; se va folosi metoda ''create()'' din cadrul clasei ''InvoiceHeaderManager'' pentru care trebuie precizate valorile ce vor fi adăugate, aceasta furnizând identificatorul înregistrării din tabelă:
* numărul de identificare (câmpul ''identification_number'') va fi generat aleator folosind metoda ''Utilities.generateIdentificationNumber()'', care primește ca parametri numărul de caractere de tip literă (3), respectiv cifră (6);
* data de emitere (câmpul ''issue_date'') - ziua curentă - va fi obținută folosind metoda [[https://docs.oracle.com/javase/8/docs/api/java/time/LocalDate.html#now--|now()]] din clasa [[https://docs.oracle.com/javase/8/docs/api/java/time/LocalDate.html|LocalDate]];
* starea (câmpul ''state'') are valoarea ''issued'' reținut de constanta ''STATE_ISSUED'' din interfața ''Constants'';
* identificatorul utilizatorului (câmpul ''user_id'') va fi determinat pe baza denumirii (stocate în obiectul ''display'') - concatenarea dintre prenumele şi numele reţinute în tabela ''user'' - prin apelul metodei ''getIdentifier()'' din clasa ''UserManager'';
* se iterează asupra conținutului coșului de cumpărături (obiectul ''shoppingCart''):
* se obține identificatorul formatului de prezentare a cărții (atributul), cantitatea (valoarea) și stocul corespunzător, așa cum este reținut în tabela ''book_presentation'' (prin apelul metodei ''getStockpile()'') din clasa ''BookPresentationManager'');
* se compară numărul de exemplare din coşul de cumpărături cu stocul existent şi în situația în care comanda poate fi satisfăcută, se actualizează stocul (se apelează metoda ''update()'' din clasa ''BookPresentationManager'' care primește ca parametri atât atributul cât și valoarea care trebuie să fie modificate);
* se adaugă o înregistrare în tabela ''invoice_line''; se va folosi metoda ''create()'' din cadrul clasei ''InvoiceLineManager'' pentru care trebuie precizate valorile ce vor fi adăugate:
* identificatorul facturii (câmpul ''invoice_header_id''), determinat anterior;
* identificatorul formatului de prezentare a cărții (câmpul ''book_presentation_id'');
* cantitatea (câmpul ''quantity'');
* se goleşte coşul de cumpărături.
În situația în care sunt realizate modificări la nivelul coșului de cumpărături (pe obiectul ''shoppingCart''), starea acestuia va trebui reținută în cadrul sesiunii, printr-un apel de tipul:
session.setAttribute(Utilities.removeSpaces(Constants.SHOPPING_CART.toLowerCase()), shoppingCart);
**12.** Să se implementeze operaţia de deautentificare printr-un buton plasat sub mesajul de întâmpinare pentru fiecare utilizator în pagina de tip client. În această situație, utilizatorul se va întoarce în pagina de autentificare.
{{ :laboratoare:laborator04:activitate_de_laborator_exercitiu12.png?nolink }}
În clasa ''ClientGraphicUserInterface'', se afișează un element de tip [[http://www.w3schools.com/tags/tag_input.asp|]], având atributul ''type'' cu valoarea ''image'' și denumirea ''signout'' (valoare reținută de constanta ''SIGNOUT'' din interfața ''Constants''). Resursa grafică se regăsește în directorul ''images'', în subdirectorul ''user_interface'', având denumirea ''signout.png''.
În clasa ''ClientServlet'', în cazul în care a fost apăsat butonul pentru operația de deautentificare:
* se obține lista cu toți parametrii (metoda [[http://docs.oracle.com/javaee/7/api/javax/servlet/ServletRequest.html#getParameterNames--|getParameterNames()]] din clasa [[http://docs.oracle.com/javaee/7/api/javax/servlet/ServletRequest.html|ServletRequest]]), aceștia fiind eliminați, prin invocarea metodei [[http://docs.oracle.com/javaee/7/api/javax/servlet/ServletRequest.html#removeAttribute-java.lang.String-|removeAttribute()]];
* se elimină elementele conținute de listele ''shoppingCart'', ''formatsFilter'', ''languagesFilter'', ''categoriesFilter'';
* valorile conținute de obiectele ''previousRecordsPerPage'', ''currentRecordsPerPage'', ''currentPage'' vor fi cele implicite;
* se elimină conținutul obiectului ''books'';
* se transferă contextul către servletul care gestionează operația de autentificare (''LoginServlet''):
RequestDispatcher dispatcher = getServletContext().getRequestDispatcher("/" + Constants.LOGIN_SERVLET_PAGE_CONTEXT);
if (dispatcher != null) {
dispatcher.forward(request, response);
}
* se invalidează sesiunea prin invocarea metodei [[https://docs.oracle.com/javaee/7/api/javax/servlet/http/HttpSession.html#invalidate--|invalidate()]] pe obiectul de tip [[https://docs.oracle.com/javaee/7/api/javax/servlet/http/HttpSession.html|HttpSession]], - după această operație nu mai este permis să se utilizeze pagina Internet curentă;
session.invalidate();
Se poate utiliza ca model implementarea din cadrul paginii de administrator (clasele ''AdministratorServlet'' / ''AdministratorGraphicUserInterface'').
===== Resurse ======
[[https://docs.oracle.com/javaee/7/JEETT.pdf|Eric Jendrock, Ricardo Cervera-
Navarro, Ian Evans, Kim Haase, William Markito - The Java EE 7 Tutorial]] - capitolele 6 (subcapitolul 6.1), 17\\
[[http://tomcat.apache.org/|Apache Tomcat]]\\
[[http://www3.ntu.edu.sg/home/ehchua/programming/howto/Tomcat_More.html|Apache Tomcat - More about the Cat]]\\
[[http://www.tutorialspoint.com/servlets/|Servlets Tutorial]]\\
[[http://www.javatpoint.com/servlet-tutorial|Servlets Tutorial]]\\
[[http://www.ntu.edu.sg/home/ehchua/programming/java/JavaServlets.html|Java Server-Side Programming]]\\
[[http://courses.coreservlets.com/Course-Materials/csajsp2.html|Building Web Apps in Java: Beginning & Intermediate Servlet & JSP Tutorials]]\\
[[http://courses.coreservlets.com/Course-Materials/msajsp.html|Advanced Servlet and JSP Tutorials]]
===== Soluții =====
[[https://github.com/aipi2015/Laborator04/tree/master/solutions/eclipse/04-BookStore-JavaServlets|Proiect Eclipse]]\\
[[https://github.com/aipi2015/Laborator04/tree/master/solutions/netbeans/04-BookStore-JavaServlets|Proiect NetBeans]]