This shows you the differences between two versions of the page.
| Both sides previous revision Previous revision Next revision | Previous revision | ||
|
laboratoare:laborator07 [2014/11/24 13:47] Andrei Roșu-Cojocaru removed |
laboratoare:laborator07 [2015/01/12 12:44] (current) Andrei Roșu-Cojocaru |
||
|---|---|---|---|
| Line 3: | Line 3: | ||
| ===== Obiective ==== | ===== 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 ==== | ===== Cuvinte Cheie ==== | ||
| - | Java Servlets, Apache Tomcat 8.x, web application, Java Enterprise Edition, request, response, servlet life cycle, servlet execution flow | + | 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 ====== | ===== Materiale Ajutătoare ====== | ||
| + | |||
| + | {{ :laboratoare:laborator07:aipi2014-lab07-slides.pdf }} | ||
| + | |||
| + | {{ :laboratoare:laborator07:aipi2014-lab07-slides-3pp.pdf}} | ||
| ===== Implementarea aplicaţiilor web folosind Java Enterprise Edition ===== | ===== Implementarea aplicaţiilor web folosind Java Enterprise Edition ===== | ||
| Line 254: | Line 266: | ||
| {{ :laboratoare:laborator07:netbeans04.png?nolink }} | {{ :laboratoare:laborator07:netbeans04.png?nolink }} | ||
| - | ===== Tehnologia Java Servlets ===== | + | ===== 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. | 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. | ||
| Line 293: | Line 305: | ||
| | cerere | cererea pentru un obiect servlet este procesată de componentele web | ''javax.servlet.ServletRequestListener'' | ''ServletRequestEvent'' | | | cerere | cererea pentru un obiect servlet este procesată de componentele web | ''javax.servlet.ServletRequestListener'' | ''ServletRequestEvent'' | | ||
| | ::: | operaţii asupra atributelor (CRUD) | ''javax.servlet.ServletRequestAtrributeListener'' | ''ServletRequestAttributeEvent'' | | | ::: | operaţii asupra atributelor (CRUD) | ''javax.servlet.ServletRequestAtrributeListener'' | ''ServletRequestAttributeEvent'' | | ||
| - | |||
| - | ==== Exemplu ==== | ||
| - | |||
| - | 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. | ||
| - | |||
| - | <note>Operaţiile care pot fi realizate de către un utilizator având rolul de administrator sunt adăugare, editare şi ştergere informaţii pentru oricare dintre tabelele din baza de date.</note> | ||
| - | |||
| - | <note>O comandă este definită prin combinaţia (''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.</note> | ||
| - | |||
| - | Î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 | ||
| - | | ||
| - | Pagina de tip administrator şi pagina de tip client din aplicaţia BookStore | ||
| ==== Structura unui Java Servlet ==== | ==== Structura unui Java Servlet ==== | ||
| Line 394: | Line 390: | ||
| * ''getServletInfo()'', folosit de servlet pentru a oferi informaţii despre el. | * ''getServletInfo()'', folosit de servlet pentru a oferi informaţii despre el. | ||
| - | + | ||
| - | Tratarea cererilor de către un servlet generic / servlet HTTP | + | {{ :laboratoare:laborator07:figura07_structura_unui_java_servlet.png?nolink&400 }} |
| 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: | 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: | ||
| Line 432: | Line 428: | ||
| Î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. | Î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. | ||
| - | ==== Funcționalitate ==== | + | ===== Implementarea unor funcționalități complexe ===== |
| - | === Filtre === | + | ==== 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: | 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: | ||
| Line 454: | Line 450: | ||
| 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). | 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 ==== |
| **Invocarea altor resurse web** poate fi realizată: | **Invocarea altor resurse web** poate fi realizată: | ||
| Line 484: | Line 480: | ||
| 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. | 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. | ||
| - | === Încărcarea de fișiere === | + | ==== Î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 ''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()''. | 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()''. | ||
| Line 501: | Line 497: | ||
| 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. | 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. | ||
| - | === Procesarea asincronă === | + | ==== 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. | 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. | ||
| Line 550: | Line 546: | ||
| 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. | 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 === | + | ==== 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. | Î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. | ||
| Line 566: | Line 562: | ||
| Metodele de tratare a evenimentelor asociate unui obiect ''WriteListener'' sunt ''void onWritePossible()'', ''void onError(Throwable t)''. | Metodele de tratare a evenimentelor asociate unui obiect ''WriteListener'' sunt ''void onWritePossible()'', ''void onError(Throwable t)''. | ||
| - | ==== Integrarea Java Servlet cu sisteme de gestiune pentru baze de date ==== | + | ==== 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. | 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. | ||
| - | <file java DataBaseWrapperImplementation.java> | + | 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. |
| - | public class DataBaseWrapperImplementation implements DataBaseWrapper { | + | |
| - | private Connection dbConnection; | + | Î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. |
| - | private DatabaseMetaData dbMetaData; | + | |
| - | private String defaultDatabase; | + | |
| - | private static DataBaseWrapperImplementation instance; | + | <code java> |
| - | + | try { | |
| - | private DataBaseWrapperImplementation() { | + | Class.forName("com.mysql.jdbc.Driver"); |
| - | } | + | } catch(ClassNotFoundException exception) { |
| - | + | System.out.println("An exception has occurred: "+exception.getMessage()); | |
| - | public static DataBaseWrapperImplementation getInstance() { | + | if(Constants.DEBUG) |
| - | if (instance == null) | + | exception.printStackTrace(); |
| - | instance = new DataBaseWrapperImplementation(); | + | |
| - | return instance; | + | |
| - | } | + | |
| - | + | ||
| - | private void openConnection() throws SQLException { | + | |
| - | try { | + | |
| - | Class.forName("com.mysql.jdbc.Driver"); | + | |
| - | } catch(ClassNotFoundException exception) { | + | |
| - | System.out.println("An exception has occurred: "+exception.getMessage()); | + | |
| - | if(Constants.DEBUG) | + | |
| - | exception.printStackTrace(); | + | |
| - | } | + | |
| - | if (dbConnection == null || dbConnection.isClosed()) { | + | |
| - | dbConnection = DriverManager.getConnection( | + | |
| - | Constants.DATABASE_CONNECTION + (defaultDatabase != null ? defaultDatabase : ""), | + | |
| - | Constants.DATABASE_USERNAME, | + | |
| - | Constants.DATABASE_PASSWORD); | + | |
| - | dbMetaData = dbConnection.getMetaData(); | + | |
| - | } | + | |
| - | } | + | |
| - | + | ||
| - | private void closeConnection() throws SQLException { | + | |
| - | if (dbConnection != null && !dbConnection.isClosed()) { | + | |
| - | dbConnection.close(); | + | |
| - | } | + | |
| - | } | + | |
| - | + | ||
| - | @Override | + | |
| - | public int getTableNumberOfColumns(String tableName) throws SQLException { | + | |
| - | int numberOfColumns = 0; | + | |
| - | openConnection(); | + | |
| - | ResultSet result = dbMetaData.getColumns(Constants.DATABASE_NAME, null, tableName, null); | + | |
| - | while (result.next()) | + | |
| - | numberOfColumns++; | + | |
| - | return numberOfColumns; | + | |
| - | } | + | |
| - | + | ||
| - | @Override | + | |
| - | public ArrayList<ArrayList<String>> getTableContent(String tableName, ArrayList<String> attributes, String whereClause, String orderByClause, String groupByClause) throws SQLException { | + | |
| - | openConnection(); | + | |
| - | Statement statement = createStatement(); | + | |
| - | String query = "SELECT "; | + | |
| - | int numberOfColumns = -1; | + | |
| - | if (attributes == null) { | + | |
| - | numberOfColumns = getTableNumberOfColumns(tableName); | + | |
| - | query += "*"; | + | |
| - | } else { | + | |
| - | numberOfColumns = attributes.size(); | + | |
| - | query = attributes.stream().map((attribute) -> attribute + ", ").reduce(query, String::concat); | + | |
| - | query = query.substring(0, query.length() - 2); | + | |
| - | } | + | |
| - | query += " FROM " + tableName; | + | |
| - | if (whereClause != null) | + | |
| - | query += " WHERE " + whereClause; | + | |
| - | if (groupByClause != null) { | + | |
| - | query += " GROUP BY " + groupByClause; | + | |
| - | } | + | |
| - | if (orderByClause != null) | + | |
| - | query += " ORDER BY " + orderByClause; | + | |
| - | if (Constants.DEBUG) | + | |
| - | System.out.println("query: " + query); | + | |
| - | if (numberOfColumns == -1) | + | |
| - | return null; | + | |
| - | ArrayList<ArrayList<String>> dataBaseContent = new ArrayList<>(); | + | |
| - | ResultSet result = statement.executeQuery(query); | + | |
| - | int currentRow = 0; | + | |
| - | while (result.next()) { | + | |
| - | dataBaseContent.add(new ArrayList<>()); | + | |
| - | for (int currentColumn = 0; currentColumn < numberOfColumns; currentColumn++) { | + | |
| - | dataBaseContent.get(currentRow).add(result.getString(currentColumn + 1)); | + | |
| - | } | + | |
| - | currentRow++; | + | |
| - | } | + | |
| - | destroyStatement(statement); | + | |
| - | return dataBaseContent; | + | |
| - | } | + | |
| - | + | ||
| - | // ... | + | |
| - | | + | |
| } | } | ||
| - | </file> | + | </code> |
| - | + | ||
| - | În exemplu, a fost definită o metodă ''getTableContent()'' care primeşte elementele unei interogări SQL (numele tabelei pe care va fi executată cererea – ''tableName'', vectorul de atribute care se doresc a fi obţinute, precum şi elementele clauzelor ''WHERE'', ''ORDER BY'' şi ''GROUP BY'') şi construieşte o matrice în care este redat conţinutul acesteia. Se observă că accesarea informaţiilor reţinute în cadrul bazei de date se face similar ca şi în cazul altor tipuri de aplicaţii, respectând API-ul JDBC (metodele ''execute()'' / ''executeQuery()'' / ''executeUpdate()'' aplicabile obiectelor de tip ''Statament'', pentru execuţia unor interogări, respectiv ''get{DataType}'' din cadrul obiectelor ''ResultSet'' pentru obţinerea unor informaţii corespunzătoare unui atribut având un anumit tip şi aflat pe o anumită poziţie în cadrul unui set de înregistrări. Metoda foloseşte la rândul ei funcţia ''getTableNumberOfRows()'' care determină numărul de atribute din cadrul unei tabele folosind informaţii obţinute din dicţionarul de date (lista coloanelor unei tabele) asociat sistemului de gestiune pentru baze de date. | + | |
| - | + | ||
| - | Prin urmare, 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 şi în acest caz spre a genera conţinutul dinamic obţinut din baza de date rezidentă pe server, fără a fi necesară instalarea de utilitare pe client, informaţiile putând fi vizualizate prin intermediul browser-ului. | + | |
| - | + | ||
| - | Întrucât aceste module vor fi plasate pe serverul web Apache Tomcat, eventualele informaţii de depanare vor putea fi vizualizate în consola acestuia. | + | |
| ==== Mecanisme pentru gestiunea stării în Java Servlets ==== | ==== Mecanisme pentru gestiunea stării în Java Servlets ==== | ||
| Line 680: | Line 588: | ||
| Problema apare în momentul când trebuie să se coreleze mai multe accesări (care provin de la acelaşi utilizator). | Problema apare în momentul când trebuie să se coreleze mai multe accesări (care provin de la acelaşi utilizator). | ||
| - | Soluţiile implică **utilizarea de câmpuri ascunse**, **rescrierea URL-urilor pentru a include parametrii suplimentari**, utilizarea de **cookie**-uri sau folosirea unor instrumente de "urmărire" a **sesiunii**. | + | 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 ''<INPUT type="hidden" ...>''), dar au dezavantajul că pot fi identificate cu uşurinţă. | **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 === | ||
| **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''. | **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. | 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. | **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. | ||
| Line 709: | Line 629: | ||
| Cookie[] cookies = request.getCookies(); | Cookie[] cookies = request.getCookies(); | ||
| </code> | </code> | ||
| + | |||
| + | === Folosirea unor instrumente de "urmărire" a sesiunii === | ||
| Î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. | Î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. | ||
| Line 726: | Line 648: | ||
| 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. | 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. | ||
| - | ===== Activitate de Laborator ===== | + | ===== Activitate de Laborator ===== |
| - | {{ :laboratoare:laborator07:aipi2014-lab07-eclipseee.zip }} | + | * {{ :laboratoare:laborator07:aipi2014-lab07-eclipseee.zip }} (proiect EclipseEE ) |
| - | {{ :laboratoare:laborator07:aipi2014-lab07-netbeans.zip }} | + | * {{ :laboratoare:laborator07:aipi2014-lab07-netbeans.zip }} (proiect NetBeans ) |
| - | {{ :laboratoare:laborator07:aipi2014-lab07-noide.zip }} | + | * {{ :laboratoare:laborator07:aipi2014-lab07-noide.zip }} (proiect NoIDE - fără mediu integrat de dezvoltare) |
| + | |||
| + | ==== 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) ş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. | ||
| + | |||
| + | <note>Operaţiile care pot fi realizate de către un utilizator având rolul de administrator sunt adăugare, editare şi ştergere informaţii pentru oricare dintre tabelele din baza de date.</note> | ||
| + | |||
| + | <note>O comandă este definită prin combinaţia (''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.</note> | ||
| + | |||
| + | Î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''). | ||
| + | |||
| + | {{ :laboratoare:laborator07:bookstore.png?nolink&300 }} | ||
| + | |||
| + | **Structura aplicaţiei BookStore** | ||
| + | |||
| + | {{ :laboratoare:laborator07:bookstore01.png?nolink&300 }} | ||
| + | |||
| + | {{ :laboratoare:laborator07:bookstore02.png?nolink&600 }} | ||
| + | |||
| + | {{ :laboratoare:laborator07:bookstore03.png?nolink&600 }} | ||
| + | |||
| + | **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''. | **1.** [**0 puncte**] Să se instaleze baza de date prin rularea script-ului ''Laborator07.sql''. | ||
| - | Conţinutul script-ului'' 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ă. | + | <note>Conţinutul script-ului'' 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ă.</note> |
| **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''. | **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''. | ||
| Line 747: | Line 695: | ||
| **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. | **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**] | + | **4.** [**0 puncte**] |
| - | ==== No IDE ==== | + | ==== Eclipse ==== |
| - | 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 creeze o referinţă către serverul HTTP Apache Tomcat care să fie adăugată la proiectul BookStore. |
| - | Să se "publice" aplicaţia BookStore în contextul serverului Apache Tomcat prin rularea script-ului ''deploy[.bat|.sh]'' din directorul ''scripts''. | + | Să se deschidă proiectul folosind opţiunea //File// → //Import// → //General// → //Existing Project into Workspace..//. |
| - | De remarcat faptul că aplicaţia ''BookStore'' se găseşte în directorul ''webapps'' al serverului Apache Tomcat. | + | 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//. |
| - | În cazul Unix, daţi drepturi de execuţie fişierului ''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ă. | + | {{ :laboratoare:laborator07:eclipse05.png?nolink&600 }} |
| - | ==== NetBeans ==== | + | {{ :laboratoare:laborator07:eclipse06.png?nolink&600 }} |
| - | 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). | + | {{ :laboratoare:laborator07:eclipse07.png?nolink&600 }} |
| - | ==== Eclipse ==== | + | ==== NetBeans ==== |
| Să se creeze o referinţă către serverul HTTP Apache Tomcat care să fie adăugată la proiectul BookStore. | 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 //Deploy// din meniul contextual al proiectului (accesat cu right-click). |
| - | 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//. | + | {{ :laboratoare:laborator07:netbeans05.png?nolink }} |
| + | |||
| + | <note tip>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.</note> | ||
| + | |||
| + | ==== No IDE ==== | ||
| + | |||
| + | 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''. | ||
| + | |||
| + | <note>De remarcat faptul că aplicaţia ''BookStore'' se găseşte în directorul ''webapps'' al serverului Apache Tomcat.</note> | ||
| + | |||
| + | <note tip>În cazul Unix, daţi drepturi de execuţie fişierului ''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ă.</note> | ||
| ===== ===== | ===== ===== | ||
| Line 775: | Line 735: | ||
| **5.** [**0 puncte**] Să se testeze aplicaţia prin accesarea adresei http://localhost:8080/BookStore/. | **5.** [**0 puncte**] Să se testeze aplicaţia prin accesarea adresei http://localhost:8080/BookStore/. | ||
| - | <note tip>Se poate consulta script-ul ''Laborator07.sql'' există exemple de utilizatori care pot fi folosite pentru accesarea paginilor de administrator, respectiv de client.</note> | + | <note tip> |
| + | Se poate consulta script-ul ''Laborator07.sql'' există exemple de utilizatori care pot fi folosite pentru accesarea paginilor de administrator, respectiv de client. | ||
| + | * administrator: daniel.wright / - | ||
| + | * client: richard.allen / - | ||
| + | </note> | ||
| **6.** [**0 puncte**] Să se acceseze pagina de administrator şi să se testeze funcţionalităţile implementate în cadrul acesteia (adăugare, editare, ştergere). | **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 clasa ''ClientGraphicUserInterface'' din pachetul ''ro.pub.cs.aipi.lab07.graphicuserinterface'' 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. | + | **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. |
| - | Pentru fiecare colecţie / domeniu pot exista mai mulţi identificatori în tabelele aferente (colecţii / domenii) care au aceeași denumire. | + | <note tip>Pentru fiecare colecţie / domeniu pot exista mai mulţi identificatori în tabelele aferente (colecţii / domenii) care au aceeași denumire. |
| - | **8.** [**35 puncte**] Î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. | + | Interogările de tipul |
| + | <code mysql> | ||
| + | SELECT id FROM series WHERE name='...'; | ||
| + | SELECT id FROM genre WHERE name='...'; | ||
| + | </code> | ||
| + | pot furniza mai mulți identificatori pentru o colecție / un domeniu. | ||
| + | </note> | ||
| + | |||
| + | <note tip>Denumirile pentru colecția / domeniul selectate din listele derulante se regăsesc în obiectele ''currentCollection'' / ''currentDomain''.</note> | ||
| + | |||
| + | <note tip>În situația în care sunt selectate toate colecțiile / domeniile (valoarea ''Constants.ALL = "all"''), criteriul respectiv nu va fi luat în considerare pentru realizarea filtrării.</note> | ||
| + | |||
| + | **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. | Î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. | ||
| Line 789: | Line 765: | ||
| 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. | 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. | ||
| - | <note tip>Parametrul care conţine identificatorul cărţii care se doreşte adăugată în coş are forma ''exemplare_idCarte'' (de exemplu, ''exemplare_1'', pentru ''idCarte=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)''. | + | <note tip>Parametrul care conţine identificatorul cărţii care se doreşte adăugată în coş are forma ''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 ''idCarte'', 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. | + | 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.</note> | 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.</note> | ||
| - | **9.** [**15 puncte**] În clasa ''ClientGraphicUserInterface'' din pachetul ''ro.pub.cs.aipi.lab07.graphicuserinterface'' să se completeze metoda ''displayClientGraphicUserInterface()'' astfel încât să se vizualizeze coţinutul coşului de cumpărături. | + | **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. |
| + | |||
| + | {{ :laboratoare:laborator07:exercitiile9_10.png?nolink }} | ||
| Î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. | Î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 clasa ''ClientGraphicUserInterface'' din pachetul ''ro.pub.cs.aipi.lab07.graphicuserinterface'' 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). | + | **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). |
| - | **11.** [**40 puncte**] În clasa ''ClientServlet'', metoda ''doPost()'', să se implementeze operaţia pentru anularea, respectiv finalizarea unei comenzi. | + | <note tip>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''; | ||
| + | </note> | ||
| + | |||
| + | **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. | ||
| <note tip>În cazul anulării unei comenzi, se şterge conţinutul obiectului ''shoppingCart''. | <note tip>În cazul anulării unei comenzi, se şterge conţinutul obiectului ''shoppingCart''. | ||
| În cazul finalizării unei comenzi, trebuie realizate următoarele operaţii: | În cazul finalizării unei comenzi, trebuie realizate următoarele operaţii: | ||
| - | * adăugarea unei înregistrări în tabela facturi; pentru generarea aleatoare a câmpului serienumar se poate folosi metoda ''Utilities.generateBillNumber();'' ca dată se va completa data curentă (prin funcţia MySQL ''CURDATE()''), starea este ''emisă'', iar CNP-ul utilizatorului va fi dedus din ''userDisplayName'' care este concatenarea dintre prenumele şi numele reţinute în tabela utilizatori; | + | * adăugarea unei înregistrări în tabela ''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''; |
| - | * pentru fiecare intrare din coşul de cumpărături: | + | * pentru fiecare element din coşul de cumpărături: |
| - | * se compară numărul de exemplare din coşul de cumpărături cu stocul din tabela ''cărţi'' şi dacă relaţia este de tipul <=, se actualizează stocul (scăzând exemplarele comercializate); | + | * se compară numărul de exemplare din coşul de cumpărături cu stocul din tabela ''book'' şi dacă relaţia este de tipul mai mic sau egal, se actualizează stocul (scăzând exemplarele comercializate); |
| - | * se adaugă o înregistrare în tabela detalii_factura; | + | * se adaugă o înregistrare în tabela ''invoice_detail''; |
| * se goleşte coşul de cumpărături. | * se goleşte coşul de cumpărături. | ||
| </note> | </note> | ||
| - | **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 administrator, respectiv client. De asemenea, utilizatorul se va întoarce în pagina de autentificare. | + | <note important> |
| + | În situația în care sunt realizate modificări la nivelul coșului de cumpărături (pe obiectul ''shoppingCart''), starea acestuia va trebui consemnată în cadrul sesiunii, printr-un apel de tipul: <code java> | ||
| + | session.setAttribute(Utilities.removeSpaces(Constants.SHOPPING_CART.toLowerCase()), shoppingCart); | ||
| + | </code> | ||
| + | </note> | ||
| + | |||
| + | **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. | ||
| + | |||
| + | {{ :laboratoare:laborator07:exercitiul12.png?nolink }} | ||
| + | |||
| + | <note tip>Se va lucra în clasele ''ClientServlet'' / ''ClientGraphicUserInterface''.</note> | ||
| + | |||
| + | <note tip>Puteți folosi ca model implementarea din cadrul paginii de administrator (clasele ''AdministratorServlet'' / ''AdministratorGraphicUserInterface'').</note> | ||
| - | <note tip>Se va lucra în clasele ''AdministratorServlet'' / ''AdministratorGraphicUserInterface'', respectiv ''ClientServlet'' / ''ClientGraphicUserInterface''.</note> | + | <note important>Înainte de transferul contextului către servletul care gestionează operația de autentificare, se vor elimina din cerere toți parametrii, se va invalida sesiunea curentă (printr-un apel al metodei ''invalidate()'' pe obiectul ''session'') și se vor îndepărta preferințele cu privire la filtre.</note> |
| ===== Resurse ====== | ===== Resurse ====== | ||
| Line 822: | Line 817: | ||
| Navarro, Ian Evans, Kim Haase, William Markito - The Java EE 7 Tutorial]] - capitolele 6 (subcapitolul 6.1), 17\\ | 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://tomcat.apache.org/|Apache Tomcat]]\\ | ||
| - | [[http://www3.ntu.edu.sg/home/ehchua/programming/howto/Tomcat_More.html|Apache Tomcat - More about the Cat]] | + | [[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 ===== | ===== Soluții ===== | ||
| - | disponibile pe 4 decembrie 2014 | + | [[https://github.com/aipi2014/Laborator07/tree/master/EclipseEE/|Proiect EclipseEE]]\\ |
| + | [[https://github.com/aipi2014/Laborator07/tree/master/NetBeans/|Proiect NetBeans]] | ||