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
O aplicaţie web este definită ca o extensie dinamică a unui server (web sau de aplicaţii).
Frecvent, acestea sunt clasificate ca:
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 Java Server Pages) într-un obiect HttpServletRequest
, fiind ulterior transmis componentei web ce poate interactiona 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 HttpServletResponse
, fiind apoi transformat într-un răspuns HTTP care este afişat mai departe în browser.
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.
Apache Tomcat este un server HTTP care oferă funcționalitatea de container pentru Java Servlets, având o arhitectură modulară.
Versiunile 8.x implementează:
De asemenea, aplicațiile web dezvoltate folosind serverul web Apache Tomcat trebuie să fie scrise folosind versiunea 7 a limbajului de programare Java.
Documentele JSP (JavaServer Pages) / JSF (JavaServer Faces) sunt convertite în mod automat în clase Java Servlets, în momentul în care aplicația web care le conține este încărcată în contextul acestuia.
Serverul Apache Tomcat operează conform modelului cerere-răspuns. O cerere este transmisă în momentul în care în navigator este solicitată o pagină Internet disponibilă în contextul serverului. Pe baza execuției clasei Java Servlet corespunzătoare, se generează un răspuns ce constă într-un document HTML.
În arhitectura Apache Tomcat, structura unui serviciu include un motor, o gazdă, un context şi un cluster. Nivelul cel mai de sus este reprezentat de container, iar nivelul cel mai de jos este reprezentat de cluster.
Un serviciu asociază unul sau mai mulţi conectori la motorul serverului. Motorul serverului Apache Tomcat poate rula pe mai multe maşini (virtuale), redirecţionând cererile către cea care este identificată prin adresa IP şi portul specificate în antetele HTTP.
Implicit, motorul folosit de Apache Tomcat este Catalina, pentru care se asociază doi conectori: HTTP şi AJP.
Un motor poate conţine una sau mai multe gazde.
La rândul ei, o gazdă poate avea mai multe contexte.
În mod implicit, Apache Tomcat defineşte o gazdă localhost
şi un director de bază webapps
unde vor fi dezvoltate toate aplicaţiile.
webapps
.
Serverul Apache Tomcat conţine următoarele directoare:
bin
– fişiere binare şi script-uri apelate la pornirea / oprirea serverului Apache Tomcat;conf
– include câte un director pentru fiecare motor ce conţine subdirectoare pentru toate gazdele gestionate de acesta și fişiere de configurare care se aplică tuturor aplicaţiilor:catalina.policy
conţine politicile de securitate aplicabile;catalina.properties
şi logging.properties
specifică diferite proprietăți;server.xml
, web.xml
, context.xml
și tomcat-users.xml
conțin configurări ale serverului, aplicațiilor, contextului și utilizatorilor care pot accesa diferite funcționalități.lib
– conţine bibliotecile comune (pe care le vor folosi toate aplicaţiile):servlet-api.jar
pentru aplicaţii ce folosesc Java Servlets;jasper.jar
/ jasper-el.jar
pentru Java Server Pages Expression Language;logs
– conţine jurnalele specifice motorului Catalina, ale gazdelor pe care le gestionează, ale aplicaţiilor manager
/ host-manager
şi jurnalul de acces.webapps
– locaţia la care vor fi plasate aplicaţiile care vor putea fi accesate folosind serverul Apache Tomcat;work
– director de lucru care va conţine clasele Java Servlet corespunzătoare documentelor Java Server Faces / Java Server Pages; organizarea sa se face pe motor, gazde configurate în cadrul motorului, aplicaţii;temp
– diferite resurse temporare.
Pentru a putea utiliza serverul Apache Tomcat, iniţial este necesară specificarea variabilelor de mediu JAVA_HOME
şi JRE_HOME
în cazul în care acestea nu sunt definite deja (în fişierul batch bin/setclasspath.bat
– Windows respectiv bin/setclasspath.sh
– Linux):
set JAVA_HOME=C:\Program Files\Java\jdk1.8.0_25\ set JRE_HOME=C:\Program Files\Java\jdk1.8.0_25\jre\
export JAVA_HOME=/usr/lib/jvm/default-java/ export JRE_HOME=/usr/lib/jvm/default-java/jre/
Ulterior, configurarea Apache Tomcat se face din fişierul conf/server.xml
, care prezintă o structură similară cu arhitectura serverului.
conf/server.xml
, acesta conţinând toate informaţiile necesare pentru rularea unei aplicaţii cu Apache Tomcat.
Atributele unpackWARs
, respectiv autoDeploy
indică dezarhivarea aplicaţiilor dezvoltate ca .war (Web Archive), respectiv instalarea lor automată, pe măsură ce sunt plasate în directorul corespunzător.
<Host name="localhost" appBase="webapps" unpackWARs="true" autoDeploy="true">
În documentul conf/tomcat_users.xml
se definesc rolurile pentru accesarea diferitelor funcţionalităţi ale Apache Tomcat precum şi utilizatorii care primesc aceste roluri.
<?xml version='1.0' encoding='utf-8'?> <tomcat-users> <role rolename="manager-gui" /> <role rolename="manager-status" /> <role rolename="manager-script" /> <role rolename="manager-jmx" /> <role rolename="admin-gui" /> <role rolename="admin-script" /> <user username="admin" password="admin" roles="manager-gui, admin-gui, manager-status, manager-script, admin-script, manager-jmx" /> </tomcat-users>
Semnificaţia rolurilor este descrisă mai jos:
manager-gui
– oferă acces la o interfaţă grafică cu utilizatorul (accesibilă la http://localhost:8080/manager/html, respectiv din secţiunea Manager App);manager-status
– pune la dispoziţia utilizatorilor informaţii cu privire la starea serverului (accesibil la http://localhost:8080/manager/server, respectiv din secţiunea Server Status);manager-script
– este o interfaţă în format text, în care se pot transmite serverului Apache Tomcat comenzi prin intermediul parametrilor din URL http://localhost:8080/manager/text/{comanda}?{parametri} unde:list
(se afişează aplicaţiile dezvoltate în contextul serverului);deploy
– dacă se doreşte dezvoltarea unei aplicaţii;manager-script
pentru dezvoltarea aplicaţiei în contextul serverului Apache Tomcat.
manager-jmx
– oferă acces la interfaţa JMX, accesibilă la http://localhost:8080/manager/jmxproxy/?{comanda}={parametri} În momentul în care rulează, serverul Apache Tomcat poate fi accesat din navigator la adresa de Internet http://localhost:8080, având următorul conținut:
Din acest context, sunt disponibile legături către:
Orice aplicaţie web dezvoltată folosind serverul Apache Tomcat va trebui plasată în directorul webapps
.
Fiecare aplicaţie conţine directoarele:
WEB-INF
având în componență fişierele legate de aplicaţie în sine:web.xml
;classes
);lib
).META-INF
conţine informaţiile legate de server – fişierul context.xml
.sources
sau src
).
lib
) trebuie să se facă explicit (prin apelarea metodei Class.forName(“…”))
, întrucât serverul Apache Tomcat nu realizează acestă operaţie în mod automat atunci când aplicaţia web este instalată.
Fişierul de configurare web.xml
descrie numele aplicaţiei web (servlet-name
) precum şi clasa care conţine servlet-ul implementând practic serviciul web corespunzător (servlet-class
). Opţional, poate fi specificat un parametru load-on-startup
care, atunci când are valoare pozitivă indică ordinea în care sunt încărcate clasele respectiv, lasă la latitudinea container-ului care conţine servlet-ul să încarce clasele atunci când este necesar, dacă este dată o valoare negativă. O astfel de clasă trebuie plasată obligatoriu în directorul classes
.
De asemenea, se va specifica o asociere între numele servletului şi contextul în care acesta va putea fi accesat (proprietatea url-pattern
din cadrul secţiunii servlet-mapping
). Pagina Internet care va fi încărcată când se va porni aplicaţia web poate fi specificată în proprietatea welcome-file
din cadrul secţiunii welcome-file-list
. În acest caz, se va indica denumirea servletului asociat paginii Internet respective şi nu contextul din care acesta poate fi accesat.
<web-app> <servlet> <description></description> <display-name>ServletName</display-name> <servlet-name>ServletName</servlet-name> <servlet-class>ServletClass</servlet-class> </servlet> <servlet-mapping> <servlet-name>ServletName</servlet-name> <url-pattern>/ServletClass</url-pattern> </servlet-mapping> <welcome-file-list> <welcome-file>ServletName</welcome-file> </welcome-file-list> </web-app>
Pagina Internet asociată servletului ServletName
va fi disponibilă la adresa http://localhost:8080/<application_directory>/ServletClass/, unde application_directory
reprezintă contextul unde a fost instalată aplicaţia web (corespunzător subdirectorului din webapps
).
Pot fi specificate mai multe clase Java Servlet, fiecare dintre acestea gestionând operațiile corespunzătoare unei pagini Internet.
Opţional, se pot construi şi script-uri pentru realizarea operaţiilor necesare configurării aplicaţiei în contextul serverului.
În exemplu, există un director scripts
care conţine fişierele batch deploy.bat
respectiv deploy.sh
. Acestea compilează sursele, le mută în directorul classes
şi reiniţializează serverul web Apache Tomcat (opreşte instanţa care rulează şi porneşte o instanţă nouă) astfel încât modificările realizate la nivelul claselor să fie vizibile în cadrul aplicaţiei. În cazul în care serverul web Apache Tomcat nu fusese pornit anterior, va fi generată o excepţie ce nu împiedică însă rularea cu succes a scripturilor.
Lansarea în execuţie a serverului Apache Tomcat se face prin intermediul fişierului batch bin/startup.bat
(Windows) respectiv bin/startup.sh
(Linux).
Oprirea serverului web Apache Tomcat se face prin bin/shutdown.bat
, respectiv bin/shutdown.sh
.
Reiniţializarea serverului web este necesară de fiecare dată dacă se utilizează tehnologia Java Servlets atunci când se realizează modificări la nivelul paginilor web deoarece clasele sunt “configurate” (eng. deployed) împreună cu dependenţele doar în momentul în care Apache Tomcat este lansat în execuţie. În cadrul acestui proces, sunt parcurse toate subdirectoarele din webapps
astfel încât fiecare instanță a unei aplicaţii web în parte este încărcată în contextul serverului web.
Nov 24, 2014 12:00:00 AM org.apache.catalina.startup.HostConfig deployDirectory INFO: Deploying web application directory BookStore
Se creează o referință către serverul Apache Tomcat 8.0 din mediul de dezvoltare Eclipse Luna IDE for Java EE Developers 4.4.1 (Window → Preferences → Server → Runtime Environments), accesându-se butonul Add….
Se indică ca tip al mediului de execuție Apache → Apache Tomcat v8.0.
Configurarea serverului Apache Tomcat 8.0 presupune precizarea:
În situația în care configurarea a fost realizată corect, referința către serverul Apache Tomcat 8.0 va fi afișată în secțiunea Server Runtime Environments.
Se creează o referință către serverul Apache Tomcat 8.0 din mediul de dezvoltare NetBeans 8.0.1 (Services → Servers → click-dreapta → Add Server…).
Se indică ca tip de server Apache → Apache Tomcat or TomEE.
Configurarea serverului Apache Tomcat 8.0 presupune precizarea:
manager
sau manager-script
(admin
/ admin
); se va bifa opțiunea Create user if it does not exist.
În situația în care configurarea a fost realizată corect, referința către serverul Apache Tomcat 8.0 va fi afișată în secțiunea Services → Servers (se indică și starea sa: pornit / oprit).
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:
service()
);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 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 GenericServlet
. Clasa HttpServlet
oferă metode precum doGet()
şi doPost()
pentru a trata servicii specifice protocolului HTTP.
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:
init()
;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.
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 |
Un Java Servlet este o clasă derivată din javax.servlet.http.HttpServlet
, suprascriind metodele init()
, destroy()
(pentru operaţiile realizate la construirea şi distrugerea obiectului instanţă a clasei), respectiv (cel mai frecvent) doPost()
şi 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<String> 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()); displayForm(printWriter); printWriter.close(); } @Override public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // ... } }
Adnotarea @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 urlPatterns
sau value
), celelalte atribute fiind opţionale, iniţializându-se cu valorile implicite.
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 initParams
al adnotării @WebServlet
ce conţine la rândul său o adnotare @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 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()
).
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:
doGet()
, dacă servlet-ul tratează cereri HTTP GET
;doPost()
, dacă servlet-ul tratează cereri HTTP POST
;doPut()
, dacă servlet-ul tratează cereri HTTP PUT
;doDelete()
, dacă servlet-ul tratează cereri HTTP DELETE
;
De regulă, metodele doOptions()
şi 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;init()
şi destroy()
sunt alocate şi dezalocate resurse partajate cum ar fi conexiunea la baza de date sau accesul la fişiere.
getServletInfo()
, folosit de servlet pentru a oferi informaţii despre el.
O cerere conţine datele transmise de la client către servlet şi trebuie să implementeze interfaţa ServletRequest
, unde sunt definite metode pentru accesarea de:
BufferedReader
creat dintr-un obiect ServletInputStream
întors de metoda getInputStream()
.
Obiectul HttpServletRequest
transmis prin cerere conţine URL-ul cererii, antetele HTTP şi interogarea formată din perechi de parametri şi valori.
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: getContextPath()
, getServletPath()
şi getPathInfo()
. Interogarea este compusă dintr-un set de parametrii şi valori ce pot fi obţinuţi din obiectul cerere folosind metodele getParameterNames()
şi 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
: getContentType()
, getInputStream()
, getParameter()
, getParameterNames()
, dar și proprii: getCookies()
, getHeaderNames()
, getHeaders()
, getSession()
, getMethod()
.
Un răspuns conţine datele transmise de la servlet către client şi trebuie să implementeze interfaţa 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 setContentType()
, al cărei parametru poate fi text/html
), dacă se aloca o zonă de memorie (metoda setBufferSize(int)
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.
PrinterWriter
întors de metoda getWriter()
, în timp ce pentru transmiterea de date binare se foloseşte un obiect ServletOutputStream
întors de metoda 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 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
: flushBuffer()
, get/setBufferSize()
, get/setContentType()
, getOutputStream()
, setCharacterEncoding()
, dar şi proprii: addCookie()
, get/setHeader()
, get/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()
.
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 <input>
- 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 displayForm()
, care primeşte un obiect de tip PrinterWriter
în care se creează (prin apeluri println()
) pagina Internet care va fi afişată în browser.
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:
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 Filter
, FilterChain
şi FilterConfig
.
Un filtru implementează interfaţa javax.servlet.Filter
şi este adnotat cu însemnarea @WebFilter
care trebuie să specifice cel puţin un URL (folosind atributele urlPatterns
sau value
). Informaţiile cu privire la configurarea unui filtru sunt precizate prin atributul initParams al aceleiaşi adnotări
. Metoda cea mai importantă a unui filtru este 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 ServletRequestWrapper
sau HttpServletRequestWrapper
. Similar, pentru răspuns va fi folosit ServletResponseWrapper
sau 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 poate fi realizată:
Pentru a invoca o resursă disponibilă pe serverul pe care rulează componenta web, trebuie obţinut un obiect RequestDispatcher
folosindu-se metoda 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 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 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 requestDispatcher = null; switch(getUserRole(userName,userPassword)) { case Constants.USER_ADMINISTRATOR: requestDispatcher = getServletContext().getRequestDispatcher("/AdministratorServlet"); break; case Constants.USER_CLIENT: requestDispatcher = getServletContext().getRequestDispatcher("/ClientServlet"); break; } if (requestDispatcher != null) requestDispatcher.forward(request,response);
Accesarea contextului web în care sunt executate componentele se face prin metoda getServletContext()
care întoarce un obiect de tipul 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.
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 javax.annotation.MultipartConfig
este folosită pentru a indica faptul că servletul pentru care este declarată poate prelucra cereri folosind tipul MIME multipart/form-date
, componentele Part
putând fi obţinute prin una din metodele request.getPart(String name)
sau request.getParts()
.
Adnotarea @MultipartConfig
suportă mai multe atribute opţionale:
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 “”;fieldSizeThreshold
– dimensiunea fişierului (în octeţi) după care acesta va fi stocat temporar pe disc; valoarea implicită este 0 octeţi;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ă;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 <multipart-config> … </multipart-config>
, denumirile câmpurilor fiind identice cu cele ale proprietăţilor adnotării.
Specificaţia Java Servlet defineşte pentru un obiect HttpServletRequest
:
Collection<Part> 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 write(String filename)
) sau ştergerea ei.
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 asyncSupported=true
în cadrul adnotării @WebServlet
. Funcţionalitatea pentru a realiza aceste operaţii este oferită de clasa 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:
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
;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;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;void complete()
– termină operaţia asincronă şi transmite răspunsul asociat cu contextul asincron; poate fi apelată după constuirea răspunsului;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.
Î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 javax.servlet.ServletInputStream
suportă metoda 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 ReadListener
sunt void onDataAvailable()
, void onAllDataRead()
, void onError(Throwable t)
.
boolean isReady()
– întoarce true dacă datele pot fi citite non-blocant;boolean isFinished()
– întoarce true când toate datele au fost citite.
Clasa javax.servlet.ServletOutputStream
suportă metodele:
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;boolean isReady()
– întoarce true
dacă datele pot fi scrise non-blocant.
Metodele de tratare a evenimentelor asociate unui obiect WriteListener
sunt void onWritePossible()
, void onError(Throwable t)
.
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(); }
Protocolul HTTP este un protocol fără stare fiind caracterizat prin cereri şi răspunsuri ca tranzacţii izolate.
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ă:
Câmpurile ascunse pot fi conţinute în formularele din paginile HTML (elemente de tip <INPUT type=“hidden” …>
), dar au dezavantajul că pot fi identificate cu uşurinţă.
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.
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 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 poate stabili denumirea şi valoarea (metodele set/get
Name
şi Value
), timpul de expirare, domeniul sau calea, acesta putând fi inclus într-un obiect de tip HttpServletResponse
:
response.addCookie(cookie);
Cookie-urile pot fi obţinute prin metoda getCookies()
implementată în clasa HttpServletRequest
.
Cookie[] cookies = request.getCookies();
În pachetul javax.servlet.http
este definită şi interfaţa 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 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 javax.servlet.http.HttpSessionBindingListenerInterface
pentru a monitoriza asocierea sau disocierea unui obiect cu o sesiune, respectiv interfaţa 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 getAttributeNames()
, respectiv getAttribute(String)
, iar stabilirea lor prin metoda setAttribute(String, Object)
. Totodată, un atribut poate fi eliminat prin metoda 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 set/get
MaxInactiveInterval
), 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 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 encodeURL(URL)
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.
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) şi de 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.
idcomanda
, idcarte
, cantitate
), unde idcomanda
reprezintă identificatorul comenzii (un număr de ordine care identifică comanda în mod unic), în timp ce idcarte
specifică un produs din cadrul comenzii respective (comanda putând conţine unul sau mai multe produse), iar cantitate
numărul de volume comandate de client.
În acest sens, au fost definite clase Java Servlet corespunzătoare paginilor din aplicaţia web: pagina de autentificare (LoginServlet
) de unde, în funcţie de rolul utilizatorului se trece la pagina de administrare (AdministratorServlet
) respectiv pagina client (ClientServlet
).
Structura aplicaţiei BookStore
Paginile de tip autentificare, administrator şi client din aplicaţia BookStore
1. [0 puncte] Să se instaleze baza de date prin rularea script-ului Laborator07.sql
.
Laborator07.sql
este identic cu cel din Laborator03.sql
, astfel că această etapă nu mai este necesară în cazul în care a fost deja realizată.
2. [0 puncte] Î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
.
aipi2014@ubuntu:~/apache-tomcat-8.0.15$ cd bin aipi2014@ubuntu:~/apache-tomcat-8.0.15/bin$ sudo chmod +x *.sh
3. [0 puncte] Să se completeze în clasa Constants
din pachetul ro.pub.cs.aipi.lab07.general
valorile pentru utilizatorul şi parola pentru accesarea bazei de date.
4. [0 puncte]
Să se creeze o referinţă către serverul HTTP Apache Tomcat care să fie adăugată la proiectul BookStore.
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 BookStore (right-click), bifându-se opţiunea Always use this server when running this project.
Să se creeze o referinţă către serverul HTTP Apache Tomcat care să fie adăugată la proiectul BookStore.
Se accesează opţiunea Deploy din meniul contextual al proiectului (accesat cu right-click).
Să se specifice variabilele de mediu JAVA_HOME
şi JAVA_JRE
în script-urile setClasspath[.bat|.sh]
(din directorul bin
al serverului Apache Tomcat).
Să se “publice” aplicaţia BookStore în contextul serverului Apache Tomcat prin rularea script-ului deploy[.bat|.sh]
din directorul scripts
.
BookStore
se găseşte în directorul webapps
al serverului Apache Tomcat.
deploy.sh
. În cazul în care calea către Java există în variabila de mediu $PATH
, puteţi comenta linia care face acest lucru precum şi apelul compilatorului javac
de la adresa absolută.
5. [0 puncte] Să se testeze aplicaţia prin accesarea adresei http://localhost:8080/BookStore/.
Laborator07.sql
există exemple de utilizatori care pot fi folosite pentru accesarea paginilor de administrator, respectiv de client.6. [0 puncte] Să se acceseze pagina de administrator şi să se testeze funcţionalităţile implementate în cadrul acesteia (adăugare, editare, ştergere).
7. [10 puncte] În pachetul ro.pub.cs.aipi.lab07.graphicuserinterface
, în clasa ClientGraphicUserInterface
să se completeze metoda displayClientGraphicUserInterface()
astfel încât conţinutul tabelei cărţi să fie filtrat după valoarea selectată din lista de colecţii / domenii.
Interogările de tipul
SELECT id FROM series WHERE name='...'; SELECT id FROM genre WHERE name='...';
pot furniza mai mulți identificatori pentru o colecție / un domeniu.
currentCollection
/ currentDomain
.
Constants.ALL = “all”
), criteriul respectiv nu va fi luat în considerare pentru realizarea filtrării.
8. [35 puncte] În pachetul ro.pub.cs.aipi.lab07.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 care există deja în coşul de cumpărături se poate actualiza cantitatea.
În situaţia în care solicitarea pentru un produs depăşeşte stocul existent, aceasta nu va fi luată în considerare, afişându-se un mesaj de eroare.
Dacă un produs există deja în coşul de cumpărături, valoarea respectivă va fi suprascrisă. Dacă valoarea este 0, produsul va fi şters din coşul de cumpărături.
copies_bookId
(de exemplu, copies_1
, pentru bookId=1
). Se verifică dacă parametrul are această formă şi în acest caz, identificatorul cărţii se obţine prin parsarea acestui câmp, iar numărul de exemplare prin apelul request.getParameter(parameter)
.
Dacă în coşul de cumpărături există o intrare cu acelaşi bookId
, aceasta este actualizată (suprascrisă / ştearsă în cazul în care numărul de exemplare este 0), altfel se introduce o nouă intrare, nu înainte de a se verifica faptul că operaţia este posibilă (numărul de exemplare solicitat nu depăşeşte stocul). În cazul în care sunt solicitate mai multe exemplare decât sunt disponibile, se va afişa un mesaj de eroare.
Coşul de cumpărături este un obiect ArrayList<Record>
unde ca atribut se reţine identificatorul cărţii, iar ca valoare numărul de exemplare.
9. [15 puncte] În pachetul ro.pub.cs.aipi.lab07.graphicuserinterface
, în clasa ClientGraphicUserInterface
, să se completeze metoda displayClientGraphicUserInterface()
astfel încât să se vizualizeze coţinutul coşului de cumpărături.
În coşul de cumpărături se va afişa numărul de exemplare comandate, titlul cărţii, editura şi anul, precum şi suma pentru fiecare produs în parte, respectiv preţul total pentru întreg coşul de cumpărături.
10. [10 puncte] În pachetul ro.pub.cs.aipi.lab07.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ă (o comandă conţine produsele selectate în coşul de cumpărături).
images
, în subdirectorul user_interface
, având denumirile:remove_from_shopping_cart.png
;shopping_cart_accept.png
;
11. [40 puncte] În pachetul ro.pub.cs.aipi.lab07.servlets
, în clasa ClientServlet
, metoda doPost()
, să se implementeze operaţia pentru anularea, respectiv finalizarea unei comenzi.
shoppingCart
.
În cazul finalizării unei comenzi, trebuie realizate următoarele operaţii:
invoice
; pentru generarea aleatoare a câmpului identification_number
se poate folosi metoda Utilities.generateInvoiceNumber();
ca dată de emitere (câmpul issue_date
) se va completa data calendaristică curentă (prin funcţia MySQL CURDATE()
), starea (coloana state
) este emisă (issued
), iar identificatorul utilizatorului (atributul personal_identifier
) va fi dedus din userDisplayName
care este concatenarea dintre prenumele şi numele reţinute în tabela user
;book
şi dacă relaţia este de tipul mai mic sau egal, se actualizează stocul (scăzând exemplarele comercializate);invoice_detail
;shoppingCart
), starea acestuia va trebui consemnată în cadrul sesiunii, printr-un apel de tipul: session.setAttribute(Utilities.removeSpaces(Constants.SHOPPING_CART.toLowerCase()), shoppingCart);
12. [10 puncte] 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.
ClientServlet
/ ClientGraphicUserInterface
.
AdministratorServlet
/ AdministratorGraphicUserInterface
).
invalidate()
pe obiectul session
) și se vor îndepărta preferințele cu privire la filtre.
Eric Jendrock, Ricardo Cervera-
Navarro, Ian Evans, Kim Haase, William Markito - The Java EE 7 Tutorial - capitolele 6 (subcapitolul 6.1), 17
Apache Tomcat
Apache Tomcat - More about the Cat
Servlets Tutorial
Servlets Tutorial
Java Server-Side Programming
Building Web Apps in Java: Beginning & Intermediate Servlet & JSP Tutorials
Advanced Servlet and JSP Tutorials