Differences
This shows you the differences between two versions of the page.
| Next revision | Previous revision | ||
|
laboratoare:laborator08 [2015/08/29 18:06] Andrei Roșu-Cojocaru created |
laboratoare:laborator08 [2015/12/11 15:19] (current) Andrei Roșu-Cojocaru |
||
|---|---|---|---|
| Line 1: | Line 1: | ||
| ====== Laborator 08 ====== | ====== Laborator 08 ====== | ||
| - | ====== Realizarea de aplicații web folosind JavaServer Pages ====== | + | ====== Proiectarea de aplicații distribuite folosind framework-ul Hadoop ====== |
| ===== Obiective ==== | ===== Obiective ==== | ||
| - | * descrierea funcționalității pe care o pune la dispoziție tehnologia JavaServer Pages și a avantajelor pe care le oferă în comparație cu alte tehnologii similare; | + | * înțelegerea modelului implementat de Hadoop pentru procesarea distribuită a unui volum mare de date, folosind clustere formate din mașini fără performanțe distribuite, larg disponibile în comerț (//eng.// commodity hardware); |
| - | * înţelegerea mecanismului prin care este generat răspunsul la o cerere pentru o pagină Internet solicitată din browser, în cazul folosirii de pagini JSP; | + | * descrierea principalelor module integrate de framework-ul Hadoop; |
| - | * integrarea unei aplicații web ce folosește Java Servlets / JavaServer Pages în contextul serverului Apache Tomcat; | + | * cunoașterea produselor care funcționează împreună cu modulele Hadoop, funcționalitatea pe care o pun la dispoziție și oportunitatea folosirii lor; |
| - | * implementarea operațiilor corespunzătoare ciclului de viață a unei pagini JSP; | + | * configurarea framework-ului Hadoop (HDFS, YARN) și a bazei de date distribuite HBase pe un cluster format dintr-o singură mașină; |
| - | * proiectarea unei aplicații web ce folosește Java Servlets / JavaServer Pages potrivit modelului arhitectural adecvat specificațiilor funcționale; | + | * utilizarea paradigmei de programare Map/Reduce pentru procesarea distribuită a unui volum mare de date; |
| - | * folosirea elementelor componente din structura unei pagini JSP (directive, acțiuni, scripleți, expresii, declarații, obiecte implicite) pentru implementarea unor aspecte de logica aplicației ce nu pot fi delegate către Java Servlets; | + | * familiarizarea cu modul în care sunt gestionate sarcinile Map/Reduce în diferite implementări ale framework-ului Hadoop (Map/Reduce "clasic", respectiv YARN); |
| - | * utilizarea unor biblioteci de etichete (predefinite - JSTL sau definite de utilizator) / API-uri (JavaMail / Java Activation Framework) / EL (Expression Language) pentru dezvoltarea unor funcționalități complexe; | + | |
| ===== Cuvinte Cheie ==== | ===== Cuvinte Cheie ==== | ||
| - | JavaServer Pages, web application, html, xml, svg, wml, markup, Java Beans, Apache Tomcat 8.x, JSP life cycle, parse exception, jasper exception, client-server model, N-tier model, model-view-controller, directive, action, scriplet, expression, declaration, implicit objects, cookie, session, filter, file upload, resource invocation, email managing, expression language, instant evaluation, lazy evaluation, JavaServer Faces, JSTL, custom tags | + | Hadoop, cluster, commodity hardware, Google File System, Hadoop Distributed File System, YARN, Map-Reduce, HBase, ZooKeeper, Oozie, Pig, Hive, HDFS federation, namenode, datanode, Job Tracker, Task Tracker, Resource Manager, Node Manager, Application Master |
| ===== Materiale Ajutătoare ====== | ===== Materiale Ajutătoare ====== | ||
| - | {{ :laboratoare:laborator08:aipi2014-lab08-slides.pdf }} | + | ===== Framework-ul Hadoop - aspecte generale ===== |
| - | {{ :laboratoare:laborator08:aipi2014-lab08-slides-3pp.pdf }} | + | Hadoop este un proiect open-source dezvoltat de Apache care îşi propune realizarea de procesări distribuite a unor seturi de date de dimensiuni mari, rulând pe mai multe clustere, folosind modele de programare simple. Proiectarea acestui framework a fost realizată astfel încât să fie scalabilă chiar şi în situaţia în care sarcinile sunt rulate pe mii de calculatoare, fiecare dintre acestea punând la dispoziţie o anumită capacitate de procesare şi de stocare. |
| - | ===== Tehnologia JavaServer Pages - aspecte generale ===== | + | <note>Un cluster este un set de mai multe maşini, legate prin intermediul unei reţele de calculatoare, care se găsesc în aceeaşi locaţie fizică. Nu este necesar ca performanţele pe care le oferă acestea să fie foarte ridicate, scalarea realizându-se nu prin adăugarea de resurse la o maşină (de vreme ce legea lui Moore oricum nu poate acoperi creşterea cantităţii de date), ci prin creşterea numărului de maşini în cadrul unui cluster, respectiv prin creşterea numărului de clustere, framework-ul Hadoop fiind proiectat pentru a gestiona astfel de modificări. Mai mult, spre diferenţă de modelul "tradițional" de procesare distribuită, nu se face o separare între noduri de procesare şi noduri de stocare (întrucât pot apărea blocaje) ci un nod implementează ambele funcţionalităţi, fiind procesate datele stocate local.</note> |
| - | JavaServer Pages este o tehnologie pentru realizarea de aplicații Internet generate dinamic şi bazate pe HTML, XML sau alte tipuri de documente. A fost lansată pe piaţă de compania Sun Microsystems în anul 1999 ca răspuns la tehnologiile PHP şi ASP, demonstrând faptul că Java este un limbaj de programare suficient de robust pentru a răspunde la provocările web-ului. | + | <note>Spre diferenţă de sistemele Grid şi Cloud, Hadoop nu se bazează pe hardware-ul maşinilor care fac parte din cluster pentru a oferi un nivel de disponibilitate ridicat, ci are capabilitatea de a detecta (la nivel software) erorile care se produc, gestionând astfel de situaţii (în special prin replicarea datelor şi re-execuţia sarcinilor), garantând realizarea sarcinilor, inclusiv prin redirecţionarea lor pe alte maşini.</note> |
| - | <note>Dacă tehnologia JavaServer Pages este comparabilă cu PHP (cu excepţia eficienţei), mai ales de când limbajul a fost dezvoltat în direcţia orientării pe obiecte, în cazul celorlalte tehnologii, JSP prezintă o serie de avantaje. Faţă de ASP .NET, avantajul tehnologiei JSP rezultă din faptul că aceasta este construită peste limbajul de programare Java, mai puternic şi mai uşor de utilizat decât limbajele Microsoft, dispunând de o portabilitate mai mare atât în privinţa sistemelor de operare cât şi a serverelor de aplicaţii. JavaScript este util în generarea de cod HTML dinamic la nivelul clientului dar nu şi la nivelul serverului, mai ales când vine vorba de accesarea de resurse existente pe acesta. De asemenea, SSI (Server Side Includes) a fost proiectat pentru incluziuni destul de simple, nu pentru programe cu funcţionalităţi complexe. Utilizarea JSP este recomandată şi faţă de folosirea exclusivă a Java Servlets întrucât codul devine mult mai uşor de întreţinut, realizându-se totodată o delimitare netă între logica aplicaţiei şi nivelul de prezentare.</note> | + | Un programator care proiectează aplicaţii distribuite folosind Hadoop nu trebuie să gestioneze evenimentele care se pot produce la nivelul unei maşini, concentrându-se pe dezvoltarea logicii aplicaţiei, în timp ce partiţionarea datelor şi distribuirea codului vor fi realizate în mod transparent prin intermediul framework-ului, interacţiunea dintre componente fiind definită în cadrul unor interfeţe standardizate. |
| - | Tehnologia JavaServer Pages e parte integrantă Java Enterprise Edition implementând de regulă intefaţa cu utilizatorul dintr-o aplicaţie web Java. | + | Hadoop a fost creat de Doug Cutting, cel care realizase şi biblioteca pentru căutări în texte Apache Lucene. Ideea acestui proiect a plecat de la Apache Nutch, un motor de căutare web open-source, lansat în 2002, care nu era însă scalabil pentru miliardele de pagini ce ar fi trebuit indexate spre a oferi rezultate corecte. Pe baza unui articol din 2003 care descria sistemul de fişiere distribuite al celor de la Google (GFS), este lansat Nutch Distributed Filesystem (NDFS) ce rezolva atât problema spaţiului de stocare al fişierelor generate în urma proceselor de indexare a paginilor Internet cât şi sarcinile administrative de gestionare a nodurilor de stocare. Un model similar este urmat şi în cazul Map-Reduce: dacă în 2004 Google publica articolul prin care era prezentat acest concept, implementarea sa în Nutch devenea deja funcţională în 2005. În 2006, odată cu mutarea lui Doug Cutting la Yahoo!, apare noţiunea de Hadoop iar în 2008 acesta era deja folosit pentru motorul de căutare web al Yahoo! folosind un cluster din 10.000 noduri. Apache acordă o importanţă deosebită proiectului, ce devine o prioritate a organizaţiei, după ce acesta devenise cel mai rapid sistem care sortase 1 TB de date (209 secunde, pe un cluster format din 910 noduri). În cadrul aceluiaşi an, recordul avea să fie depăşit de încă două ori: implementarea MapReduce de la Google sortase 1TB de date în 68 de secunde, în timp ce sistemul celor de la Yahoo! reuşise aceeaşi performanţă în numai 62 de secunde. Investiţiile în cercetare destinate acestui proiect devin din ce în ce mai mari, achiziţionându-se clustere ce depăşesc 1000 de noduri pe care sunt încărcate seturi de date ce atingeau ordinul sutelor de terrabytes. Începând cu anul 2010, Hadoop a fost adoptat pe scară largă de organizaţii atât în scopul de a stoca volume mari de date cât şi ca platformă de analiză a acestora. |
| + | În prezent, Hadoop este folosit de numeroase companii pentru care volumul de date generat zilnic depăşeşte capacităţile de procesare şi stocare specifice sistemelor convenţionale: Adobe, AOL, Amazon.com, EBay, Facebook, Google, LinkedIn, Twitter, Yahoo. | ||
| - | O pagină JSP este un document text care cuprinde două tipuri de date: **statice** care pot fi descrise în orice format (HTML, XML, SVG, WML) – denumite şi elemente de adnotare (//eng.// markup) şi **dinamice**, care pot fi directive JSP şi scripleţi, adică blocuri de cod sursă Java (de obicei cuprinse între adnotările ''%%<%%%'' şi ''%%%>%%'') folosite pentru implementarea unor funcţionalităţi complexe, cum ar fi, de exemplu, comunicaţia cu o bază de date, reţinerea preferinţelor utilizatorilor, accesarea componentelor Java Beans, transferul controlului între pagini şi partajarea informaţiei între cereri, răspunsuri şi pagini. | + | Framework-ul Hadoop include mai multe module: |
| + | * **Hadoop Common**: utilitare de bază care oferă funcţionalităţile pentru celelalte module; | ||
| + | * **Hadoop Distributed File System (HDFS)**: sistem de fişiere distribuite ce pune la dispoziţie un nivel de disponibilitate ridicat la datele utilizate de aplicaţii; | ||
| + | * **Hadoop YARN**: modul pentru planificarea sarcinilor şi gestiunea resurselor din cadrul unui cluster; | ||
| + | * **Hadoop Map-Reduce**: sistem bazat pe YARN pentru procesarea paralelă a unor seturi mari de date. | ||
| - | Funcţionalitatea pe care o pune la dispoziţie tehnologia JSP este aceeaşi cu a aplicaţiilor implementate folosind CGI (Common Gateway Interface), distingându-se de acestea prin performanţă îmbunătăţită (posibilitatea integrării de elemente dinamice în pagina HTML în loc să se folosească fişiere separate), compilarea înainte de plasarea în contextul serverului, posibilitatea accesării tuturor funcţionalităţilor oferite de interfeţele de programare Java (JDBC, JNDI, JAXP, EJB), utilizarea unui model care implică delegarea logicii aplicaţiei către Java Servlets. | + | În plus, au fost dezvoltate mai multe produse care pot fi folosite împreună cu Hadoop / HDFS: |
| + | * **HBase**: bază de date distribuită, scalabilă care suportă stocarea informaţiilor structurate pentru tabele de dimensiuni mari; implementată sub forma unor perechi cheie-valoare, foloseşte de obicei sistemul de fişiere distribuit HDFS deşi poate fi folosit şi împreună cu sistemul de fişiere local; | ||
| + | * **ZooKeeper**: serviciu de coordonare performant pentru aplicaţii distribuite; | ||
| + | * **Oozie**: modulul pentru gestiunea şi planificarea fluxurilor, coordonând fluxurile Map-Reduce; | ||
| + | * **Pig**: limbaj de nivel înalt pentru procesarea fluxurilor de date şi mediu de execuţie pentru prelucrări paralele; | ||
| + | * **Hive**: depozit de date cu interfaţă SQL care oferă sumarizarea datelor şi interogări ad-hoc. | ||
| - | <note>În cazul CGI/Perl este necesar ca serverul să încarce un interpretor şi un script de fiecare dată când o pagină este solicitată în browser.</note> | + | Există mai mulţi producători care pun la dispoziţie distribuţii Hadoop, al căror scop este oferirea unei configuraţii care rezolvă incompatibilităţile dintre diferite produse, prin rularea unor teste de integrare între acestea. |
| - | Avantajele utilizării JavaServer Pages includ: | + | Produsele Hadoop integrate în cele mai multe dintre distribuţii sunt HDFS, MapReduce, HBase, Hive, Mahout, Oozie, Pig, Sqoop, Whirr, ZooKeeper, Flume. De asemenea, proiectul BigTop (dezvoltat de Apache) are rolul de a rula teste de interoperabilitate între componentele Hadoop oferind pachete Linux (RPM şi pachete Debian) pentru o instalare mai facilă. |
| - | * separarea logicii aplicaţiei de prezentare (întreţinerea este mai uşoară atât din partea programatorilor, cât şi a dezvoltatorilor web); logica aplicaţiei este folosită pentru a genera conţinutul dinamic şi poate fi realizată prin componente Java Beans, în timp ce interfaţa cu utilizatorul este formată prin etichete JSP; | + | |
| - | * portabilitate (asigurată de limbajul de programare Java – aplicaţia web poate fi utilizată pe mai multe platforme cu arhitectură diferită fără a fi necesară modificarea lor); | + | |
| - | * conţinutul dinamic care poate fi generat; | + | |
| - | * se bazează pe tehnologia Java Servlets, astfel încât preia şi avantajele pe care le oferă aceasta; tehnologia JavaServer Pages reprezintă o abstractizare de nivel înalt a Java Servlets; totodată, în pagina JSP poate fi reutilizat codul care a fost dezvoltat într-un Java Servlet. | + | |
| - | Deşi din punctul de vedere al programatorului un Java Servlet reprezintă o clasă Java (unde generarea elementelor de adnotare se face prin apelarea unor metode) iar o pagină JSP se aseamănă mai mult cu un document (unde conţinutul static este separat de conţinutul dinamic – generat de componente Java Beans sau etichete JSP), tehnologiile sunt echivalente. O pagină JSP este transformată într-un Java Servlet (care este compilat) responsabil de comunicaţia cu clientul şi de toate celelalte prelucrări. Modificările realizate asupra unei pagini JSP sunt detectate în mod automat, astfel încât atât procesul de transformare într-un Java Servlet cât şi procesul de compilare corespunzător sunt reluate atunci când se face o cerere pentru aceasta. | + | Distribuţiile sunt realizate în mai multe formate, suportă un set de sisteme de operare şi pot include scripturi suplimentare pentru rularea mediului de lucru. |
| - | {{ :laboratoare:laborator08:figura01_interactiunea_client_server.png?nolink&600 }} | + | <note>De regulă, este vorba despre sisteme de operare Linux (RedHat Enterprise, CentOS, Oracle Linux, Ubuntu, SUSE Linux Enterprise Server).</note> |
| - | Atunci când **1)** un client transmite (din browser) o cerere HTTP către server pentru o pagină .jsp, **2)** aceasta este transmisă mai departe motorului JSP care încarcă de pe disc resursa care a fost indicată prin intermediul URL-ului. Dacă pe server nu există un Java Servlet corespunzător celei mai recente versiuni a paginii respective, **3)** acesta este generat (prin instrucţiuni de tip ''println'' ce includ etichetele respective şi convertirea elementelor JSP în cod Java care implementează comportamentul dinamic al paginii) şi **4)** apoi compilat într-o clasă executabilă. Acest cod, căruia i se pasează cererea originală **5)** este rulat de motorul Java Servlet producând un rezultat în format HTML care este transmis serverului ca răspuns HTTP, acesta **6)** fiind pasat clientului care îl afişază în browser, ca şi când ar fi conţinut static. | + | Între distribuţiile mai cunoscute se numără Cloudera Distribution for Hadoop (CDH), MapR Distribution, Hortonworks Data Platform (HDP), Apache BigTop Distribution, Greenplum HD Data Computing Appliance. Acestea dispun şi de propriile documentaţii (inclusiv wiki) şi oferă utilizatorilor maşini virtuale pe care sunt instalate distribuţiile în cauză. |
| - | {{ :laboratoare:laborator08:figura02_generare_JavaServlet_paginaJSP.png?nolink&700 }} | + | Versiunile Hadoop întreţinute în prezent sunt 1.2.x, 2.6.x şi 0.23.x (similară cu 2.x, fără nivelul de disponibilitate ridicat pentru nodurile de nume). Îmbunătăţirile aduse în cadrul versiunii 2.6.x sunt: |
| + | * implementarea **federaţiilor HDFS**, ce utilizează mai multe noduri (spaţii) de nume independente (care nu necesită coordonare între ele); nodurile de date sunt utilizate ca spaţii de stocare pentru blocuri de către toate nodurile de nume. Pentru implementarea unei astfel de funcţionalităţi, este necesar ca fiecare nod de date să fie înregistrat de toate nodurile de nume din cluster. Acestea vor transmite rapoarte periodice cu privire la blocurile pe care le stochează, gestionând comenzile care provin de la nodurile de nume.\\ \\ {{ :laboratoare:laborator08:01hdfs_federations.png?nolink }} \\ \\ Din acest motiv, nodurile de date stochează blocuri aparţinând tuturor federaţiilor de blocuri (//eng.// block pool) din cadrul cluster-ului, fiecare dintre ele fiind gestionate separat de alte astfel de obiecte. O federaţie de blocuri este reprezentată de un set de blocuri ce aparţine unui singur spaţiu de nume. În acest mod, un spaţiu de nume poate genera identificatori pentru blocuri fără a fi necesară coordonarea cu alte spaţii de nume şi în acelaşi timp, erorile produse la nivelul unui nod de nume nu împiedică nodul de date de a lucra cu alte noduri de nume din cluster. Un spaţiu de nume împreună cu federaţia sa de blocuri poartă denumirea de volum al spaţiului de nume, acesta fiind o unitate de gestiune autonomă. Atunci când un nod (spaţiu) de nume este şters, şi federaţia sa de blocuri de la nivelul nodurilor de date este ştearsă. Actualizările de la nivelul volumului unui spaţiu de nume se realizează unitar, atunci când se produce actualizarea unui cluster. Toate nodurile din cadrul unui cluster partajează un identificator al clusterului, generat în mod automat la formatarea unui nod de nume. Un astfel de identificator ar trebui să fie folosit şi pentru formatarea altor noduri de nume în cadrul cluster-ului respectiv.\\ \\ Beneficiile acestei îmbunătăţiri includ **scalabilitatea spaţiului de nume** (spaţiul de stocare al clusterului HDFS este caracterizat prin scalabilitate, astfel încât proiectele de dimensiuni mari sau cele care utilizează numeroase fişiere de dimensiuni mai mici pot beneficia de scalabilitatea spaţiului de nume prin adăugarea mai multor noduri de nume la cluster), **performanţa** (îmbunătăţirea ratei de transfer pentru operaţiile de citire / scriere în cadrul sistemului de fişiere prin adăugarea mai multor noduri de nume în cadrul clusterului) şi **izolarea** (categorii diferite de aplicaţii şi utilizatori pot fi izolate în cadrul unor spaţii de nume diferite). Într-o arhitectură care foloseşte un singur spaţiu de nume, într-un mediu concurent, o aplicaţie poate suprasolicita spaţiu de nume astfel încât performanţele celorlalte aplicaţii ce utilizează acelaşi spaţiu de nume vor fi afectate. | ||
| - | ===== Integrarea JavaServer Pages cu serverul Apache Tomcat 8.x ===== | + | * **implementarea MapReduce NextGen** (**YARN, MRv2**) prin care se împart funcţionalităţile modulului de monitorizare a sarcinilor (//eng.// Job Tracker) – gestiunea resurselor şi planificarea sarcinilor în procese separate; în acest mod, se creează **un modul global de gestiune al resurselor** (Resource Manager) şi **un modul de bază al aplicaţiei** (Application Master) pentru fiecare aplicaţie în parte. O aplicaţie este formată fie dintr-o singură sarcină (din punctul de vedere al sarcinilor de tip MapReduce) sau conţine un graf aciclic de sarcini. Modulul de gestiune al resurselor împreună cu **modulul de gestiune al nodurilor** (Node Manager) formează cadrul de calcul al sarcinilor. Modulul de gestiune al resurselor este autoritatea care arbitrează modul de distribuire al resurselor pentru aplicaţiile din sistem. Modulul de gestiune pentru aplicaţii este de fapt o bibliotecă ce negociază resursele primite de la modulul de gestiune al resurselor colaborând cu modulele de gestiune a nodurilor pentru a executa şi pentru a monitoriza sarcinile.\\ \\ {{ :laboratoare:laborator08:02yarn_architecture.png?nolink }} \\ \\ Modulul pentru gestiunea resurselor cuprinde două componente de bază: **modulul de planificare** şi **modulul de gestiune al aplicaţiilor**.\\ \\ Modulul de planificare este responsabil pentru alocarea resurselor către diferite aplicaţii, caracterizate prin constrângeri referitoare la capacitate, cozi. Acesta nu oferă însă nici o garanţie în privinţa repornirii sarcinilor eşuate datorită erorilor produse la nivel hardware sau software şi nici nu se ocupă cu monitorizarea stării aplicaţiilor. Planificarea se realizează în funcţie de cerinţele pentru resurse ale aplicaţiilor, specificate de noţiunea abstractă container care cuprinde elemente ca memorie, procesor, disc, reţea. În versiunile anterioare ale Hadoop, singurul criteriu luat în considerare de planificator era reprezentat de memorie. Modulul de planificare suportă extensii programabile (precum CapacityScheduler sau FairScheduler) responsabile cu partiţionarea clusterului între diferite aplicaţii. Extensia programabilă CapacityScheduler suportă cozi ierarhice pentru a permite partajarea resurselor clusterului în funcţie de previziuni.\\ \\ Modulul de gestiune al aplicaţiilor se ocupă cu acceptarea sarcinilor transmise, negociind container-ul pentru executarea modulului de bază al aplicaţiei, oferind servicii pentru repornirea container-ului acestuia în situaţia în care s-au produs erori.\\ \\ Modulul de gestiune al nodurilor este un cadru implementat pe fiecare maşină în parte, responsabil pentru containere, monitorizându-le utilizarea resurselor (procesor, memorie, disc, reţea) şi raportând aceste valori la modulul de gestiune al resurselor şi la modulul de planificare.\\ \\ Modulul de bază al aplicaţiei are rolul de a negocia containerele de resurse corespunzătoare de la modulul de planificare, monitorizându-le starea. |
| - | În cazul unei aplicaţii web folosind tehnologia JSP, aceasta va fi plasată în directorul ''webapps''. | + | <note>MapReduce v2 menţine compatibilitatea cu versiunile anterioare, astfel încât acestea pot rula şi în noul cadru de execuţie după ce sunt recompilate.</note> |
| - | Paginile ''.jsp'' se vor găsi în rădăcina directorului unde a fost dezvoltată aplicaţia web. Pagina care va fi afişată automat atunci când se solicită adresa: ''%%http://<server_IP>:<server_port>/<application_directory>/%%'' este ''index.jsp'' sau cea precizată prin elementul ''<welcome-file>'' din secţiunea ''<welcome-file-list>'' a fişierului de configurare ''web.xml''. Acesta va fi plasat în directorul ''WEB-INF'', specificându-se diferiţi parametrii ai aplicaţiei web (inclusiv despre clasele Java Servlet ce implementează logica aplicaţiei). De asemenea, în condiţiile în care sunt folosite metode din clase Java, acestea vor fi plasate într-un director ''WEB-INF/classes''. Este recomandat ca acestea să fie modularizate pe pachete pentru ca referirea lor să se poată realiza cu uşurinţă. Dacă se folosesc biblioteci speciale (în arhive ''.jar''), acestea trebuie incluse în ''WEB-INF/lib''. Astfel de biblioteci speciale pot fi "driver"-ul de conectare la baza de date precum şi biblioteca pentru accesarea funcţionalităţilor oferite de JSTL (JavaServer Pages Standard Tag Library). Deşi sursele aplicaţiei se pot găsi în orice fişier, este recomandat ca acestea să se găsească tot aici, într-un subdirector denumit ''src'' sau ''sources'', astfel încât să poată fi identificat cu uşurinţă, iar procesul de compilare al surselor şi de transfer al claselor (realizat înainte de operaţia de configurare – //eng.// deploy a aplicaţiei web) să nu implice un efort deosebit. | + | Funcţionalităţile implementate de diferitele versiuni Hadoop, cu privire la HDFS şi MapReduce pot fi sintetizate astfel: |
| - | {{ :laboratoare:laborator08:figura03_structura_unei_aplicatii_web.png?nolink }} | + | <html> |
| + | <center> | ||
| + | </html> | ||
| - | Încărcarea tuturor acestor clase se face atunci când este pornit serverul: | + | ^ Funcţionalitate ^ 1.2.x ^ 0.23.x ^ 2.6.x ^ |
| - | <code> | + | | autentificare sigură | da | nu | da | |
| - | Dec 4, 2014 12:00:00 AM org.apache.catalina.startup.HostConfig deployDirectory | + | | nume de configurare vechi | da | învechite | învechite | |
| - | INFO: Deploying web application directory ApacheTomcat\apache-tomcat-8.0.15\webapps\BookStore | + | | nume de configurare noi | nu | da | da | |
| - | </code> | + | | API MapReduce vechi | da | da | da | |
| + | | API MapReduce nou | da (biblioteci lipsă) | da | da | | ||
| + | | cadru de rulare MapReduce 1 (clasic) | da | da | nu | | ||
| + | | cadru de rulare MapReduce 2 (YARN) | nu | nu | da | | ||
| + | | federaţii HDFS | nu | nu | da | | ||
| + | | nivel de disponibilitate HDFS ridicat | nu | nu | da | | ||
| - | Spre diferenţă de Java Servlets, nu este necesară realizarea configurării aplicaţiei web atunci când sunt realizate modificări asupra paginilor JSP, întrucât acestea sunt detectate automat, determinând generarea unor Java Servlets corespunzătoare atunci când paginile sunt accesate de către client. Repornirea serverului Apache Tomcat va fi necesară doar în cazul operării unor modificări pentru clasele Java ce sunt utilizate de aplicaţie, fiind necesară reîncărcarea lor (după ce compilarea lor este realizată de către programator). | + | <html> |
| + | </center> | ||
| + | </html> | ||
| + | |||
| + | Decizia de a utiliza Hadoop împreună cu HDFS şi MapReduce trebuie să se bazeze pe adecvarea unei anumite probleme la acest framework. Astfel, aplicaţiile dezvoltate folosind această tehnologie implică procesarea unui volum foarte mare de date care poate fi realizată în paralel, ceea ce presupune independenţa acestora unele de altele. Situaţia în care datele sunt dependente unele de altele poate fi rezolvată prin iterare (rularea mai multor sarcini MapReduce, în care datele de ieşire ale unora reprezintă datele de intrare pentru celelalte) sau prin partajare (folosind o bază de date distribuită şi nu variabile distribuite întrucât acestea există doar în contextul unei singure maşini virtuale, neputând fi accesate şi din contextul altor noduri sau clustere). | ||
| - | Clasele Java Servlet generate pentru fiecare aplicaţie pot fi vizualizate în ''%CATALINA_HOME%\work\Catalina\<server_IP>\<application_directory>\org\apache\jsp''. | + | ===== Configurarea Mediului de Lucru ===== |
| - | Corespondentul unei pagini ''page.jsp'' va fi un Java Servlet ''page_jsp.java''. Tot aici vor fi plasate şi clasele obţinute prin compilarea acestor Java Servlets. Atât operaţia de transformare din pagină JSP în Java Servlet cât şi compilarea claselor Java Servlet generate este realizată automat de către serverul Apache Tomcat. | + | [[:utile:hadoop_hbase|Configurarea Mediului de Lucru]] |
| - | Datorită faptului că o cerere pentru o pagină JSP implică (în unele situaţii) transformarea într-un Java Servlet precum şi compilarea acestuia, vizualizarea acesteia se va face după o perioadă mai mare decât în situaţia când aceste prelucrări nu mai trebuie realizate. De asemenea, trebuie avută în vedere situaţia în care o astfel de operaţie eşuează, astfel încât în browser sunt afişate excepţiile generate în loc de conţinutul paginii solicitate. | + | ===== Paradigma de Programare Map Reduce ===== |
| - | Versiunea Apache Tomcat 8.0.15 suportă specificaţia JSP 2.3 şi EL 3.0. | + | MapReduce reprezintă un model de progamare destinat procesării de date pe un număr foarte mare de noduri, reprezentate de maşini disponibile în comerţ, fără performanţe deosebite (//eng.// commodity hardware). Este inspirat din programarea funcţională de unde sunt preluate funcţiile map şi reduce, putând fi implementat în limbaje de programare ca Java, C++, Python sau Ruby. Un astfel de model este util mai ales pentru prelucrarea unor seturi de date (semistructurate şi orientate pe înregistrări) de dimensiuni foarte mari, în care procesarea paralelă este singura soluţie pentru obţinerea unor rezultate într-un interval de timp acceptabil. |
| - | ===== Ciclul de viață al unei pagini JSP ===== | + | MapReduce se bazează pe împărţirea procesării în 2 etape: **map** şi **reduce**, fiecare primind ca date de intrare o pereche cheie-valoare (al căror tipuri poate fi stabilită de programator) şi întorcând ca rezultat tot o pereche cheie-valoare. |
| - | O pagină JSP tratează cererile asemenea unui servlet. Acesta este motivul pentru care ciclul de viaţă (ca de altfel şi multe alte capabilităţi) al paginilor JSP (în special aspectele dinamice) este asemănător cu cel al tehnologiei Java Servlet. | + | <note tip>Alternativ, se mai poate defini şi o funcţie de combinare (fiind de fapt o implementare a funcţiei de reducere), cu excepţia faptului că tipurile de intrare sunt aceleaşi ca ale funcţiei de reducere iar tipurile de ieşire sunt aceleaşi ca ale funcţiei de mapare. |
| - | În plus faţă de etapele caracteristice Java Servlets (creare, iniţializare, gestiune cereri şi răspunsuri, distrugere), mai există şi etapa de compilare. Atunci când o cerere este asociată unei pagini JSP, aceasta este tratată de un servlet special care verifică dacă servlet-ul asociat paginii JSP este mai vechi decât pagina în cauză, situaţie în care îl generează şi îl compilează. Astfel, un avantaj pe care îl au paginile JSP faţă de Java Servlets este că procesul de compilare este realizat automat. | + | ''combine: (K<sub>2</sub>, list(V<sub>2</sub>)) → list(K<sub>2</sub>, V<sub>2</sub>)'' |
| - | Odată ce pagina JSP a fost transformată şi compilată, servlet-ul corespunzător va urma ciclul de viaţă corespunzător (încărcarea claselor, instanţierea lor, apelul metodei ''init()'' pentru iniţializarea resurselor (partajate) şi încărcarea unor fişiere de configurare, execuţia (interacţiunea cu clientul şi realizarea procesărilor prin metoda ''service()''), precum şi apelul metodei ''destroy()'' dacă servlet-ul nu mai este necesar). Metodele (generate) se vor numi, în acest caz, ''_jspInit()'', ''_jspService()'', ''_jspDestroy()''. Metodele ''jspInit()'' şi ''jspDestroy()'' (apelate o singură dată în ciclul de viaţă al unei pagini JSP) pot fi suprascrise ca declaraţii JSP (cuprinse între ''%%<%!%%'' şi ''%%%>%%''). Nu se recomandă însă suprascrierea metodei ''_jspService()'' întrucât aceasta este generată în mod automat prin transformarea elementelor JSP în codul Java corespunzător, respectiv prin apelul metodei ''println()'' (a obiectului ''PrintWriter'' asociat) pentru generarea etichetelor. | + | De cele mai multe ori, funcţiile de combinare şi reducere se confundă. |
| + | </note> | ||
| - | <code java> | + | Valorile care sunt procesate de programul MapReduce sunt partiţionate în funcţie de cheile care le caracterizează şi distribuite nodurilor care aplică funcţia de mapare, în urma căreia se generează o listă (intermediară) de valori, fiecare dintre ele având asociată şi o cheie. Acestea sunt sortate şi grupate în funcţie de cheie (toate valorile care au aceeaşi cheie sunt concatenate într-o singură listă), astfel încât funcţia de reducere primeşte mai puţine perechi cheie-valoare (pentru fiecare cheie unică există o listă de valori generate anterior) obţinându-se o listă de rezultate compusă din perechi chei-valoare. |
| - | public void _jspInit() { | + | |
| - | // ... | + | |
| - | } | + | |
| - | public void _jspDestroy() { | + | ''map: (K<sub>1</sub>, V<sub>1</sub>) → list(K<sub>2</sub>, V<sub>2</sub>)'' |
| - | // ... | + | |
| - | } | + | |
| - | public void _jspService (final javax.servlet.http.HttpServletRequest request, final javax.servlet.http.HttpServletResponse response) throws java.io.IOException, javax.servlet.ServletException { | + | ''reduce: (K<sub>2</sub>, list(V<sub>2</sub>)) → list(K<sub>3</sub>, V<sub>3</sub>)'' |
| - | // ... | + | |
| - | } | + | |
| - | </code> | + | |
| - | {{ :laboratoare:laborator08:figura04_ciclul_de_viata_al_unei_pagini_jsp.png?nolink&400 }} | + | {{ :laboratoare:laborator08:00mapreduce_paradigm.png?nolink&700 }} |
| - | Toate aceste metode fac parte din interfaţa ''javax.servlet.HttpJspPage''. | + | ===== Gestiunea Sarcinilor Map/Reduce în Implementări ale Framework-ului Hadoop ===== |
| - | În cadrul procesului de compilare, motorul JSP verifică dacă este necesar să compileze pagina respectivă (în situaţia în care aceasta nu a fost niciodată compilată, sau eticheta de timp a servlet-ului corespunzător nu corespunde celei mai recente modificări), acestă operaţie fiind precedată de transformarea paginii, ce include şi parsarea acesteia. | + | Framework-ul Hadoop plasează sarcinile (în urma partiţionării) pe nodurile care găzduiesc segmentele de date care trebuie să fie procesate de acea sarcină specifică (codul este mutat acolo unde se găsesc datele). |
| - | Atât procesul de transformare şi cât şi procesul de compilare pot genera erori care sunt scoase în evidenţă doar atunci când pagina este accesată pentru prima dată: | + | Fluxurile de intrare şi ieşire sunt coordonate de framework-ul Hadoop, acesta realizând şi sincronizarea (prin intermediul unei bariere) înainte de procesul de sortare şi grupare prin care datele intermediare sunt distribuite către alte maşini. |
| - | * dacă eroarea apare în timpul transformării, serverul va întoarce excepţia ''ParseException'' iar clasa servlet va fi incompletă, astfel că ultima linie incompletă va fi un indicator către elementul JSP incorect; | + | |
| - | * dacă eroarea se produce atunci când pagina JSP este compilată (dacă avem o eroare de sintaxă în scriptlet), serverul va întoarce excepţia ''JasperException'' precum şi un mesaj care include numele servlet-ului asociat paginii JSP şi linia la care s-a constatat eroarea. | + | |
| - | Atunci când se execută o pagină JSP pot apărea excepţii, acestea trebuind tratate separat, în cadrul unei pagini dedicate unei astfel de acţiuni: | + | Erorile sunt considerate un eveniment probabil să se întâmple, astfel că sarcinile care se găsesc într-o astfel de situaţie vor fi executate din nou, însă pe alte maşini. |
| - | <code java> | + | Execuţia unei sarcini MapReduce se realizează prin apelul metodei ''submit()'' pe un obiect de tip ''Job'' care abstractizează procesarea ce se doreşte a fi realizată. |
| - | <%@ page errorPage="errorpage.jsp" %> | + | |
| - | </code> | + | |
| - | Pentru ca excepţia (într-un obiect de tip ''javax.servlet.jsp.JspException'') să fie disponibilă în cadrul paginii de eroare pentru a o trata, trebuie specificată, la începutul paginii de eroare, directiva: | + | <note tip>Alternativ, poate fi apelată metoda ''waitForJobCompetion()'' care transmite sarcina dacă aceasta nu a fost transmisă deja, aşteptând să fie terminată.</note> |
| - | <code java> | + | Implementarea aplicaţiei utilizând această paradigmă de programare depinde de parametrii de configurare specificaţi: ''mapred.job.tracker'' este folosit pentru versiunile Hadoop mai vechi, folosind un mecanism MapReduce "clasic", în timp ce ''mapreduce.framework.name'' reprezintă proprietatea utilizată pentru versiunile Hadoop mai noi, care implementează YARN. |
| - | <%@ page isErrorPage="true|false" %> | + | |
| - | </code> | + | |
| - | În etapa de **iniţializare**, care se execută o singură dată (după crearea servletului asociat paginii JSP), este apelată metoda ''_jspInit()'' unde se realizează conexiunea la baza de date, sunt încărcate diferite resurse şi se fac alte operaţii ce ţin de configurarea aplicaţiei web. În general, aici sunt iniţializate obiectele pentru accesarea funcţionalităţilor puse la dispoziţie de diferite biblioteci (frecvent, Java Server Pages Standard Tag Library), însă programatorul are posibilitatea de a realiza propriile operaţii, prin suprascrierea metodei ''jspInit()'' care va fi inclusă în cadrul acestei funcţii. | + | <note tip>Valorile posibile pentru proprietatea ''mapreduce.framework.name'' sunt: |
| + | * ''local'' (în caz că se doreşte ca sarcina să fie rulată local, în cadrul unei singure maşini virtuale Java); | ||
| + | * ''classic'' (pentru implementarea MapReduce "clasică", folosind procesele ''JobTracker'' şi ''TaskTrackers''); | ||
| + | * ''yarn'' (pentru implmentarea ce utilizează YARN). | ||
| + | </note> | ||
| - | Etapa de **execuţie** constă în generarea unui răspuns pentru fiecare cerere prin apelul metodei ''_jspService()''. Aceasta va trata fiecare dintre metodele HTTP prin care clientul comunică cu serverul (''GET'', ''POST'', ''PUT'', ''DELETE'', ''OPTIONS'', ''TRACE''). | + | În cazul **implementării MapReduce "clasice"** sunt implicate mai multe entităţi: |
| + | * clientul, care transmite sarcina de tip MapReduce; | ||
| + | * un proces pentru monitorizarea sarcinii (//eng.// Job Tracker) care coordonează rularea acesteia; | ||
| - | În etapa de **distrugere**, corespunzătoare procesului de înlăturare a paginii JSP din container, este apelată metoda ''_jspDestroy()'' unde sunt eliberate resursele utilizate de aplicaţie, astfel încât acestea să fie procesate de modulul de gestiune a memoriei. În situaţia în care se doreşte eliberarea manuală a acelor resurse care au fost iniţializate (conexiunea la baza de date, diverse fişiere de configurare), poate fi suprascrisă metoda ''jspDestroy()'', conţinutul ei fiind copiat în cuprinsul funcţiei generate. | + | <note>Procesul de monitorizare a sarcinii este o aplicaţie Java a cărei clasă principală este ''JobTracker''.</note> |
| - | ===== Arhitectura unei aplicații Internet folosind JavaServer Pages ===== | + | * mai multe procese pentru monitorizarea părţilor în care a fost împărţită sarcina (//eng.// Task Tracker); |
| - | Combinarea de cod Java cu etichete HTML oferă posibilitatea realizării de pagini cu conţinut dinamic, însă în cazul aplicaţiilor cu funcţionalitate complexă, procesul de întreţinere poate fi dificil, problema provenind în special din faptul că partea de logică a aplicaţiei (care este dezvoltată de programatori – implicând algoritmi şi interacţiune cu baza de date) nu este separată de partea de prezentare (realizată de regulă de dezvoltatori web – concentrându-se mai ales pe elemente de grafică). Arhitectura aplicaţiei JSP va reflecta raportul dintre aceste aspecte. | + | <note>Procesul de monitorizare a unei părţi a sarcinii este tot o aplicaţie Java a cărei clasă principală este ''TaskTracker''.</note> |
| - | {{ :laboratoare:laborator08:figura05_arhitectura_unei_aplicatii_web.png?nolink&800 }} | + | * sistemul distribuit de fişiere (de obicei HDFS), utilizat pentru partajarea fişierelor între aceste entităţi. |
| - | În modelul 1, logica aplicaţiei este implementată în clase Java (componente Java Beans) şi poate fi apelată din partea de prezentare realizată prin pagini JSP. Această soluţie este adecvată pentru cazul în care aplicaţia nu este foarte complexă, o problemă fiind constituită de faptul că toată comunicaţia cu clientul trebuie realizată din pagina JSP. O astfel de abordare se mai numeşte client-server, are avantajul simplităţii şi dezavantajul lipsei de scalabilitate. | + | {{ :laboratoare:laborator08:03classic_mapreduce.png?nolink }} |
| - | În modelul 2 (denumit şi abordare pe N-niveluri datorită separării arhitecturii serverului pe mai multe niveluri), se respectă şablonul de proiectare Model View Controller unde servlet-ul se ocupă de cererea de la client, implementează logica aplicaţiei, realizând totodată şi instanţierea componentelor Java Beans. Pagina JSP obţine date de la componentele Java Beans şi transmite un răspuns către client, prelucrările fiind realizate în mod transparent. Acest tip de abordare e adoptat atunci când aplicaţia este mai complexă, caracterizându-se prin uşurinţa de întreţinere şi eficienţă. | + | Etapele urmate la crearea unei sarcini de tip MapReduce sunt următoarele: |
| - | Aşadar, în cadrul unei pagini JSP se utilizează o combinaţie între etichete HTML (sau XML) şi blocuri de cod sursă Java pentru generarea de elemente dinamice. Fiecare pagină JSP este compilată într-un servlet, acesta primind cererea şi transmiţând mai departe răspunsul. | + | **1.** metoda ''submit()'' apelată pe obiectul de tip ''Job'' creează o instanţă a unui obiect de tip ''JobSummiter'' pentru care apelează metoda ''submitJobInternal()''; ulterior, metoda ''waitForJobCompletion()'' verifică progresul sarcinii o dată pe secundă, raportând starea acesteia (în situaţia în care s-a modificat de la ultimul raport); când sarcina este încheiată cu succes se afişează statisticile sarcinii, altfel este afişată eroarea care a determinat eşecul sarcinii. |
| - | O pagină JSP poate conţine etichete de prezentare (în limbajul HTML), etichete JSP standard şi etichete definite de utilizator. | + | **2.** procesul de transmitere al sarcinii (reprezentat de obiectul instanţă al clasei ''JobSummitter'') solicită procesului de urmărire al sarcinii (''JobTracker'') un nou identificator pentru sarcina respectivă (prin apelul metodei ''getNewJobId()''); ulterior, sunt verificate specificaţiile privind datele de ieşire şi datele de intrare pentru sarcina în cauză; dacă directorul unde trebuie plasate datele de ieşire nu a fost specificat sau există deja, respectiv nu pot fi determinate partiţiile pentru datele de intrare (datorită faptului că acestea nu au fost specificate sau nu există) sarcina nu este transmisă, generându-se în schimb o eroare; dacă specificaţiile privind operaţiile de intrare/ieşire sunt corecte, sunt determinate partiţiile care vor fi prelucrate de fiecare parte a sarcinii; |
| - | JSP foloseşte ca etichete standard: | ||
| - | * directive; | ||
| - | * acţiuni; | ||
| - | * scripleţi: | ||
| - | * expresii; | ||
| - | * declaraţii. | ||
| - | |||
| - | {{ :laboratoare:laborator08:figura06_structura_unei_pagini_jsp.png?nolink&1024 }} | ||
| - | De asemenea, pot fi folosite **comentarii** care pot fi: | + | **3.** procesul de transmitere al sarcinii copiază din sistemul distribuit de fişiere resursele necesare pentru rularea sarcinii (inclusiv fişierul .jar care conţine codul corespunzător sarcinii, fişierul de configurare şi partiţiile datelor de intrare) în sistemul de fişiere al procesului de urmărire al sarcinii, într-un director având numele dat de identificatorul sarcinii; |
| - | * comentarii JSP: acestea sunt ingorate de motorul JSP; <code java> | + | |
| - | <%-- comentariu; --%> | + | |
| - | </code> | + | |
| - | * comentarii HTML: acestea sunt ignorate de browser; <code java> | + | |
| - | <!-- comentariu --> | + | |
| - | </code> | + | |
| - | ==== Directive ==== | + | <note>Arhiva .jar conţinând codul corespunzător sarcinii este copiat folosind un factor de replicare foarte mare (indicat de proprietatea ''mapred.submit.replication'' a cărei valoare implicită este 10), astfel încât vor exista suficient de multe copii în cadrul clusterului ce vor putea fi accesate de procesele ce prelucrează porţiuni ale sarcinii atunci când se află în execuţie.</note> |
| - | O **directivă** este o instrucţiune care indică informaţii generale despre pagina respectivă. Aceasta afectează întreaga structură a clasei servlet. Directiva oferă container-ului diverse informaţii cu privire la modul în care va gestiona anumite aspecte ale procesării paginii JSP. | + | **4.** procesul de transmitere al sarcinii informează procesul de urmărire a sarcinii de faptul că aceasta poate fi executată, prin apelul metodei ''submitJob()'' pe obiectul instanţă a clasei ''JobTracker''; |
| - | <code java> | + | **5.** atunci când obiectul instanţă a clasei ''JobTracker'' primeşte un apel al metodei ''submitJob()'', pune sarcina respectivă într-o coadă internă de unde îl va prelua planificatorul de sarcini pentru a-l iniţializa; procesul de iniţializare presupune crearea unui obiect reprezentând sarcina care va fi executată, conţinând partiţiile care vor fi rulate pe noduri diferite, precum şi informaţii referitoare la progresul acestora; |
| - | <%@ directive attribute="value" %> | + | |
| - | </code> | + | |
| - | Directivele pot avea un număr variabil de perechi atribut-valoare, acestea fiind separate prin virgulă. Spaţiile dintre ''%%<%@%%'' şi numele directivei, respectiv dintre ultima pereche atribut-valoare şi ''%%%>%%'' sunt opţionale. | + | **6.** pentru a crea lista partiţiilor sarcinii, planificatorul de sarcini primeşte datele de intrare, defalcate pe secţiuni, aşa cum au fost împărţite de client în urma primirii acestora de la sistemul distribuit de fişiere; pentru fiecare secţiune este creată o partiţie a sarcinii (operaţie de tip ''map''), în timp ce numărul de operaţii de tip ''reduce'' este determinat din proprietatea ''mapred.reduce.task'' a obiectului ''Job'' (fiind stabilit de metoda ''setNumReduceTasks()'') creându-se un număr corespunzător de procese care le vor executa; fiecare partiţie a sarcinii (de tip map sau reduce) va putea fi urmărită printr-un identificator unic; în plus, vor mai fi create încă două procese, pentru configurarea (//eng.// setup), respectiv eliberarea resurselor (//eng.// cleanup) asociate partiţiei sarcinii, ele fiind rulate înainte, respectiv după ce codul asociat partiţiei sarcinii este executat; pentru fiecare sarcină există un obiect de tip ''OutputCommitter'' ce determină codul corespunzător fiecărei sarcini în parte; |
| - | Există trei tipuri de directive: | + | |
| - | * ''%%<%@ page ... %>%%'' – defineşte atribute specifice paginii JSP, cum ar fi limbajul de programare pentru scripturi, pagina corespunzătoare erorilor şi cerinţe legate de stocarea temporară; | + | |
| - | * ''%%<%@ include ... %>%%'' – include un fişier în cadrul etapei de transformare a paginii JSP în servletul corespunzător; | + | |
| - | * ''%%<%@ taglib ... %>%%'' – defineşte o bibliotecă de etichete, conţinând acţiuni definite de utilizator, care vor fi utilizate în cadrul paginii. | + | |
| - | === Directiva page === | + | <note>În mod implicit, obiect ''OutputCommitter'' are tipul ''FileOutputCommitter''; în cazul procesului de configurare, acesta va crea directorul în care vor fi plasate datele de ieşire precum şi directoarele de lucru (temporare), în timp ce pentru procesul de eliberare al resurselor, acesta va şterge directoarele de lucru (temporare), transmiţând în acelaşi timp rezultatul sarcinii, ceea ce pentru procesele care realizează operaţii pe fişiere se traduce în scrierea datelor de ieşire la locaţia corespunzătoare sarcinii respective (în cazul în care este activată execuţia speculativă, se asigură faptul că vor fi transmise numai rezultatele provenind de la sarcinile duplicate, celelalte fiind ignorate).</note> |
| - | Directiva ''page'' este utilizată pentru a oferi containerului informaţii despre pagina JSP curentă. Cu toate că această directivă poate fi plasată oriunde în cadrul paginii, se recomandă ca ea să fie inclusă la început. | + | **7.** procesele de urmărire a partiţiilor în care este împărţită o sarcină rulează o buclă care transmite mesaje periodice (//eng.// heartbeat) procesului ce urmăreşte sarcina prin care acesta este anunţat asupra stării sale de funcţionare; informaţiile cuprinse în aceste mesaje sunt disponibilitatea de a rula o nouă partiţie a sarcinii, caz în care, ca răspuns la acest mesaj, procesul de urmărire a sarcinii îi va aloca una, după o selecţie corespunzătoare, potrivit algoritmului de planificare pe care îl implementează; iniţial este selectată sarcina de unde va fi aleasă o partiţie pentru a fi repartizată procesului de monitorizare corespunzător, algoritmul implicit bazându-se pe asocierea unor priorităţi pentru fiecare sarcină în parte, de unde vor fi alese apoi partiţiile; fiecare proces pentru monitorizarea partiţiei unei sarcini dispune de un număr fix de sarcini de tip map respectiv de tip reduce pe care le pot rula simultan; în distribuirea sarcinilor de tip map se are în vedere localizarea (în reţea) nodului pe care rulează procesul ce urmează să realizeze procesarea, alegându-se unele pentru care setul de date se găseşte cât mai aproape; o astfel de constrângere nu este luată în calcul pentru sarcinile de tip reduce; |
| - | Sintaxa acestei directive poate lua următoarele forme: | + | <note tip>Poziţiile corespunzătoare sarcinilor de tip map vor fi completate înaintea poziţiilor aferente sarcinilor de tip reduce (cu alte cuvinte, pentru un proces de urmărire a partiţiilor unei sarcini, atâta timp cât există poziţii libere pentru sarcini de tip map, nu vor fi completate poziţiile alocate sarcinilor de tip reduce).</note> |
| - | <code java> | + | <note>Valoarea numărului de sarcini care pot fi rulate în paralel este dată de capacitatea de procesare a maşinii pe care rulează procesul respectiv cât şi de memoria respectivă.</note> |
| - | <%@ page attribute="value" %> | + | |
| - | </code> | + | |
| - | <code java> | + | <note>Ideal este ca datele să se găsească cât mai aproape de nodul care urmează să realizeze procesarea, pe acelaşi maşină sau pe acelaşi rastel. Nu întotdeauna o astfel de constrângere poate fi satisfăcută, într-o astfel de situaţie înregistrându-se o creştere a traficului utilizat în reţeaua de calculatoare.</note> |
| - | <jsp:directive.page attribute="value" /> | + | |
| - | </code> | + | |
| - | Atributele suportate de directiva ''page'' sunt: | + | **8.** după ce procesului de monitorizare a partiţiei sarcinii i-a fost alocată sarcina pe care trebuie să o ruleze, acesta îşi va copia arhiva .jar conţinând codul care va fi executat din contextul sistemului distribuit de fişiere în sistemul de fişiere local precum şi alte fişiere necesare din zona de memorie tampon distribuită; ulterior, creează un director temporar pentru sarcină, dezarhivând conţinutul fişierului .jar, acesta fiind rulat în contextul unui obiect de tipul ''TaskRunner'', instanţiat în acest scop; |
| - | | ''autoFlush'' | controlează comportamentul buffer-ului asociat servletului în cazul în care se umple (conţinutul său va fi golit automat – valoarea ''true'' (implicită) sau se generează o eroare – valoarea ''false''); se foloseşte de regulă în asociere cu atributul ''buffer'': <code java> | + | **9.** obiectul de tipul ''TaskRunner'' lansează o maşină virtuală Java nouă; |
| - | <%@ page buffer="8kb" autoFlush="true" %> | + | |
| - | <%@ page autoFlush="false" %> | + | |
| - | </code> | | + | |
| - | | ''buffer'' | specifică un model pentru fluxul de ieşire: valoarea ''none'' indică faptul că răspunsul va fi transmis imediat clientului; alte valori specifică dimensiunea maximă (în octeţi) a buffer-ului, răspunsul fiind construit în această zonă tampon înainte de a fi transmis clientului <code java> | + | |
| - | <%@ page buffer="none" %> | + | |
| - | <%@ page buffer="8kb" %> | + | |
| - | </code> | | + | |
| - | | ''contentType'' | specifică schema de codificare a caracterelor pentru pagina JSP şi pentru răspunsul generat <code java> | + | |
| - | <%@ page contentType="text/html" %> | + | |
| - | <%@ page contentType="text/xml" %> | + | |
| - | <%@ page contentType="application/msword" %> | + | |
| - | <%@ page contentType="text/html:charset=ISO-8859-1" %> | + | |
| - | </code> | | + | |
| - | | ''errorPage'' | defineşte un URL către pagina care raportează excepţiile Java netratate, generate la rulare <code java> | + | |
| - | <%@ page errorPage="errorpage.jsp"%> | + | |
| - | </code> | | + | |
| - | | ''isErrorPage'' | specifică dacă pagina JSP curentă este indicată de proprietatea ''errorPage'' a altei pagini JSP <code java> | + | |
| - | <%@ page isErrorPage="false" %> | + | |
| - | <%@ page isErrorPage="true" %> | + | |
| - | </code> | | + | |
| - | | ''extends'' | specifică o superclasă pe care servlet-ul generat trebuie să o extindă <code java> | + | |
| - | <%@ page extends="myPackage.MyClass" %> | + | |
| - | </code> | | + | |
| - | | ''import'' | indică o listă de pachete sau de clase Java care vor fi utilizate în pagina JSP; în mod implicit, sunt importate automat ''java.lang.*'', ''javax.servlet.*'', ''javax.servlet.jsp.*'', ''javax.servlet.http.*''; dacă se doreşte importarea mai multor pachete sau clase (inclusiv definite de utilizator), acestea vor fi separate prin virgulă: <code java> | + | |
| - | <%@ page import="java.sql.*, java.util.*, java.io.*" %> | + | |
| - | </code> | | + | |
| - | | ''info'' | defineşte un şir de caractere care poate fi accesat prin metoda ''getServletInfo()'' a servlet-ului asociat <code java> | + | |
| - | <%@ page info="Some info on my JSP Page" %> | + | |
| - | </code> | | + | |
| - | | ''isELIgnored'' | specifică dacă expresiile EL (Expression Languge) din pagina JSP de forma ''${...}'' vor fi evaluate (valoarea ''true'', implicită) sau vor fi tratate ca text static <code java> | + | |
| - | <%@ page isELIgnored="true" %> | + | |
| - | <%@ page isELIgnored="false" %> | + | |
| - | </code> | | + | |
| - | | ''isScriptingEnabled'' | determină dacă script-urile (scripleţi, expresii non-EL şi declaraţii) sunt permise pentru a fi utilizate (valoarea ''true'' – implicită) sau nu (valoarea ''false'') <code java> | + | |
| - | <%@ page isScriptingEnabled="true" %> | + | |
| - | <%@ page isScriptingEnabled="false" %> | + | |
| - | </code> | | + | |
| - | | ''isThreadSafe'' | defineşte modelul firului de execuţie pentru servlet-ul generat; implicit, paginile JSP sunt socotite ca fiind sigure (valoarea ''true'') pentru a fi folosite într-un context concurent, iar în caz contrar (valoarea ''false''), container-ul se asigură ca pagina să fie accesată de un singur client la un moment dat <code java> | + | |
| - | <%@ page isThreadSafe="true" %> | + | |
| - | <%@ page isThreadSafe="false" %> | + | |
| - | </code> | | + | |
| - | | ''language'' | defineşte limbajul de programare utilizat în pagina JSP <code java> | + | |
| - | <%@ page language="java" %> | + | |
| - | <%@ page language="javascript" %> | + | |
| - | </code> | | + | |
| - | | ''session'' | specifică dacă pagina JSP participă (valoarea ''true'') sau nu (valoarea ''false'') la sesiuni HTTP, adică dacă poate accesa sau nu obiectul implicit ''session'' şi metodele asociate acestuia <code java> | + | |
| - | <%@ page session="true" %> | + | |
| - | <%@ page session="false" %> | + | |
| - | </code> | | + | |
| - | === Directiva include === | + | **10.** fiecare sarcină va fi rulată în contextul acestei maşini virtuale Java, astfel că orice erori sau funcţiile de tip map/reduce definite de utilizator nu vor afecta procesul de monitorizare a partiţiei sarcinii, implicând comportamente nedorite (încetarea funcţionării); fiecare partiţie a sarcinii comunică cu procesul părinte prin interfaţa ombilicală, informându-l de progresul său la fiecare câteva secunde până când aceasta este terminată. |
| - | Directiva ''include'' este folosită pentru a include resursa specificată în fişierul curent, în cadrul etapei de transformare a paginii JSP în servlet. Practic, se concatenează conţinutul acestor fişiere externe, la locaţia unde a fost specificată (oriunde în pagina JSP): | + | <note tip>Există posibilitatea ca aceeași maşină virtuală Java să fie refolosită între mai multe partiţii ale sarcinii.</note> |
| - | <code java> | + | Întrucât sarcinile de tip MapReduce durează o perioadă mai mare de timp, este important ca utilizator să aibă informaţii cu privire la progresul acestora. Astfel, fiecare sarcină şi partiţiile sale sunt caracterizate printr-o stare (în execuţie, terminată cu succes, eşuată din cauza unei erori), progresul operaţiilor de tip map şi reduce, valorile contoarelor asociate sarcinii în cauză precum şi un mesaj (descriere) asociat stării respective. Progresul, adică proporţia din sarcină care a fost executată deja (dacă poate fi determinată), este comunicată periodic clientului. |
| - | <%@ include file="myJSPPage.jsp" %> | + | |
| - | </code> | + | |
| - | <code java> | + | Un progres înregistrat de o partiţie a sarcinii este transmisă procesului care o monitorizează prin intermediul unui semafor (//eng.// flag). Acesta e verificat de un fir de execuţie dedicat la fiecare trei secunde şi în condiţiile în care valoarea sa o impune, procesul de monitorizare a partiţiei sarcinii este informat de progresul realizat. De asemenea, în mesajele transmise către procesul de monitorizare al sarcinii sunt transmise şi date cu privire la progresul înregistrat de toate partiţiile sarcinii monitorizate în mod curent. |
| - | <jsp:directive.include file="myJSPPage.jsp" /> | + | |
| - | </code> | + | |
| - | Se consideră că resursa specificată este indicată prin calea sa relativă, astfel încât atunci când ea lipseşte, se consideră că fişierul se găseşte în acelaşi director cu pagina JSP din care este invocat. | + | <note tip>De regulă, astfel de mesaje de semnalizare sunt transmise o dată la fiecare cinci secunde, însă este posibil ca pentru clustere de dimensiuni mai mari, această perioadă să fie mai mare de vreme ce mesajele ocupă lăţime de bandă în cadrul reţelei de calculatoare care conectează nodurile.</note> |
| - | O astfel de funcţionalitate este folosită mai ales pentru includerea de resurse comune pentru toate paginile Internet cum ar fi: header, footer sau meniuri ale aplicaţiei web. | + | Valorile referitoare la progres sunt agregate de procesul de monitorizare al sarcinii, rezultând o viziune de ansamblu asupra tuturor sarcinilor rulate la un anumit moment de timp şi asupra partiţiilor constituente. Ulterior obiectul de tip ''Job'' primeşte cel mai recent statut interogând procesul de monitorizare al sarcinii în fiecare secundă. |
| - | === Directiva taglib === | + | <note tip>Clienţii pot folosi și metoda ''getStatus()'' a obiectului de tip ''Job'', obţinând o instanţă a unui obiect de tip ''JobStatus'', care conţine toate informaţiile cu privire la starea sarcinii.</note> |
| + | |||
| + | Când procesul de monitorizare a sarcinii primeşte notificări provenind de la toate partiţiile sarcinii cu privire la încheierea acestora (inclusiv sarcina pentru eliberarea resurselor), va modifica statutul asociat acesteia ca fiind "terminată cu succes", acesta fiind transmis şi obiectului de tip ''Job'', care informează la rândul său utilizatorul cu privire la acest fapt, părăsind metoda ''waitForCompletion()''. Toate statisticile şi contoarele cu privire la sarcină sunt afişate la consolă în acest moment. La final, procesul de monitorizare a sarcinii eliberează toate resursele utilizate temporar instruind şi procesele care gestionează partiţiile sarcinii să acţioneze în acelaşi sens. | ||
| - | Directiva ''taglib'' permite definirea de către utilizator a unor etichete JSP care implementează o anumită funcţionalitate, acestea fiind grupate în cadrul unor biblioteci. Se specifică pentru fiecare set de etichete locaţia la care se află biblioteca respectivă (atributul ''uri''), identificând un mecanism (atributul ''prefix'') prin care acestea vor fi identificate în cadrul paginii JSP: | + | <note>Procesul de monitorizare a sarcinii poate să transmită o notificare cu privire la starea acesteia şi prin HTTP, în situaţia în care este configurată proprietatea ''job.end.notification.url'' de către clienţii care doresc să primească astfel de informaţii prin intermediul unor apeluri inverse (//eng.// callback).</note> |
| - | <code java> | + | Întrucât pentru clustere de dimensiuni foarte mari (peste 4000 de noduri) sistemul MapReduce "clasic" are probleme de scalabilitate, a fost implementat **mecanismul YARN** (Yet Another Resource Negociator / YARN Application Resource Negociator) care rezolvă astfel de inconveniente, separând responsabilităţile procesului de monitorizare a sarcinilor (planificarea sarcinii cât şi monitorizarea progresului realizat de partiţiile asociate acesteia) în două procese independente: |
| - | <%@ taglib uri="uri" prefix="prefixOfTag" %> | + | * un proces pentru gestiunea resurselor în cadrul clusterului; |
| - | </code> | + | * un proces pentru gestiunea aplicaţiilor pe parcursul ciclului lor de viaţă. |
| - | <code java> | + | <note tip>Pentru fiecare instanţă a unei aplicaţii există un proces de gestiune dedicat, care rulează atâta vreme cât aceasta se află în execuţie.</note> |
| - | <jsp:directive.taglib uri="uri" prefix="prefixOfTag" /> | + | |
| - | </code> | + | |
| - | Pot fi folosite biblioteci standard (care implementează controlul fluxului într-un program, care pun la dispoziţie mecanisme pentru gestiunea informaţiilor dintr-o bază de date sau care implementează diferite metode uzitate mai frecvent în cadrul oricărei aplicaţii) sau se pot crea biblioteci definite de utilizator: | + | Aceste procese negociază căror noduri (containere) le vor fi alocate anumite programe, respectându-se constrângerile cu privire la capacitatea lor de procesare şi referitoare la memoria disponibilă. Verificarea acestor restricţii este realizată de procesul de gestiune al nodului, ce asigură faptul că nu sunt utilizate mai multe resurse decât au fost alocate. |
| - | <code java> | + | <note>Planificarea sarcinii se referă la asocierea partiţiilor asociate acestora unor anumite procese folosind nişte mecanisme de prioritate.</note> |
| - | <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> | + | |
| - | <%@ taglib prefix="sql" uri="http://java.sun.com/jsp/jstl/sql" %> | + | |
| - | <%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %> | + | |
| - | <%@ taglib prefix="my" uri="http://www.mysite.com/mylibrary" %> | + | |
| - | </code> | + | |
| - | Utilizarea unei astfel de etichete se face cu ''<prefixName:tagName ...>'' unde ''prefixName'' este valoarea specificată pentru atributul ''prefix'' de la definirea bibliotecii respective, iar ''tagName'' numele unei etichete implementate. | + | <note>Monitorizarea progresului realizat de partiţiile asociate unei sarcini implică păstrarea evidenţei acestora, cu repornirea sarcinilor care nu au generat erori sau a celor care progresează într-un ritm necorespunzător şi determinarea statisticilor cu privire la progresul lor, atâta timp cât se află în execuţie.</note> |
| - | ==== Acțiuni ==== | + | YARN este mai generic decât MapReduce, aceasta reprezentând doar un tip de aplicaţie ce poate fi rulată folosind acest mecanism, flexibilitatea sa constând în coexistenţa (în cadrul aceluiaşi cluster) al unor programe având tipuri diferite (de exemplu MPI sau un shell distribuit), ceea ce implică beneficii pentru gestiunea şi utilizarea resurselor respective. De asemenea, utilizatorii pot rula diferite versiuni ale MapReduce, astfel că actualizarea de la o versiune la alta se poate realiza foarte uşor. |
| - | O **acţiune** este un marcaj care specifică comportamentul motorului Java Servlets de la nivelul serverului web, în momentul compilării marcajele fiind înlocuite cu codul sursă Java corespunzător acţiunii. | + | Entităţile implicate la rularea unei aplicaţii de tip MapReduce folosind YARN sunt: |
| + | * clientul, care transmite sarcina de tip MapReduce; | ||
| + | * procesul pentru gestiunea resurselor (//eng.// Resource Manager) care coordonează alocarea resurselor de procesare pe cluster; | ||
| + | * procesele pentru gestiunea nodurilor (//eng.// Node Manager) care lansează în execuţie şi monitorizează containerele în cadrul maşinilor din cluster; | ||
| + | * procesul de gestiune a aplicaţiilor (//eng.// Application Master) care va coordona partiţiile sarcinilor; | ||
| - | Sintaxa unei acţiuni este: | + | <note tip>Atât procesul de gestiune a aplicaţiilor cât şi partiţiile sarcinii MapReduce rulează în cadrul unor containere care sunt alocate de procesul de gestiune al resurselor, fiind monitorizate de procesele de gestiune a nodurilor.</note> |
| - | <code java> | + | * sistemul distribuit de fişiere (de obicei HDFS), utilizat pentru partajarea fişierelor între aceste entităţi. |
| - | <jsp:actionName attribute="value" /> | + | |
| - | </code> | + | |
| - | Atributele comune tututor tipurilor de acţiuni sunt: | + | {{ :laboratoare:laborator08:04yarn_mapreduce.png?nolink }} |
| - | * ''id'': identifică în mod unic acţiunea respectivă permiţând ca aceasta să poată fi referită în cadrul paginii JSP; dacă acţiunea este folosită pentru crearea unei instanţe a unui obiect, prin intermediul identificatorului aceasta poate fi utilizată pe tot parcursul obiectului implicit ''PageContext''; | + | |
| - | * ''scope'': defineşte ciclul de viaţă al acţiunii, adică vizibilitatea elementului concretizată în posibilitatea de utilizare a identificatorului acestuia; valorile pe care le poate sunt: ''page'', ''request'', ''session'' şi ''application''. | + | |
| - | O acţiune este de fapt o funcţie predefinită, tipurile de acţiuni implementate de API-ul JSP fiind: | + | Etapele urmate la crearea unei sarcini de tip MapReduce în cadrul YARN sunt următoarele: |
| - | | ''<jsp:attribute>'' | defineşte atributul unui element XML definit dinamic | | + | **1.** transmiterea sarcinii se realizează folosind acelaşi API ca în cazul mecanismului MapReduce clasic, deşi există şi o implementare a ''ClientProtocol'' care este activată atunci când proprietatea ''mapreduce.framework.name'' are valoarea ''yarn'' în care procesul de transmitere a sarcinii este destul de similar cu cel al mecanismului MapReduce clasic; |
| - | | ''<jsp:body>'' | defineşte corpul unui element XML definit dinamic | | + | |
| - | | ''<jsp:element>'' | defineşte elemente XML în mod dinamic | | + | |
| - | | Prin acţiunile ''<jsp:attribute>'', ''<jsp:body>'' şi ''<jsp:element>'' elementele XML pot fi generate în momentul rulării, ca răspuns la o cerere şi nu atunci când sunt compilate sursele. <code java> | + | |
| - | <jsp:element name="xmlElement"> | + | |
| - | <jsp:attribute name="xmlAttribute"> | + | |
| - | Attribute Value | + | |
| - | </jsp:attribute> | + | |
| - | <jsp:body> | + | |
| - | Body for XML element | + | |
| - | </jsp:body> | + | |
| - | </jsp:element> | + | |
| - | </code> → <code java> | + | |
| - | <xmlElement xmlAttribute="Attribute Value"> | + | |
| - | Body for XML element | + | |
| - | </xmlElement> | + | |
| - | </code> || | + | |
| - | | ''<jsp:useBean>'' | găsirea sau instanţierea unei componente Java Bean; se caută dacă este vizibil vreun obiect având identificatorul specificat şi în cazul în care acesta nu este găsit, se încearcă instanţierea clasei indicate; ulterior, se pot folosi acţiunile ''setProperty'' şi ''getProperty'' pentru a stabili şi pentru a obţine proprietăţile componentei JavaBean\\ \\ Atributele asociate acţiunii ''useBean'' sunt:\\ • ''class'' – desemnează numele componentei Java Bean, incluzând şi pachetul în care se găseşte aceasta;\\ • ''type'' – indică tipul variabilei ce va reda obiectul în cauză\\ • ''beanName'' – specifică numele componentei Java Bean, aşa cum este indicat de metoda ''instantiate()'' a clasei ''java.beans.Beans''. <code java> | + | |
| - | <jsp:useBean id="myObject" class="myPackage.myClass" /> | + | |
| - | </code> | | + | |
| - | | ''<jsp:getProperty>'' | obţinerea valorii proprietăţii unei componente pe care o converteşte apoi la tipul şir de caractere şi apoi o integrează în fluxul de ieşire\\ \\ Atributele obligatorii asociate acţiunii ''getProperty'' sunt:\\ • ''name'' – desemnează componenta Java Bean a cărei proprietate va fi stabilită; trebuie să aibă aceeaşi valoare cu a identificatorului precizat în acţiunea ''useBean'';\\ • ''property'' – indică proprietatea care se doreşte a fi stabilită; <code java> | + | |
| - | <jsp:useBean id="myObject" class="myPackage.myClass" /> | + | |
| - | ... | + | |
| - | <jsp:getProperty name="myObject" property="myProperty"/> | + | |
| - | </code> | | + | |
| - | | ''<jsp:setProperty>'' | stabilirea valorii proprietăţii unei componente Java Bean; anterior utilizării acestei proprietăţi, componenta Java Bean referită trebuie să fi fost definită; poate fi utilizată în cadrul acţiunii ''useBean'' (situaţie în care se execută numai dacă a fost instanţiat un obiect nou) sau independent de ea (caz în care se execută atât atunci când a fost găsit un element cu numele respectiv atunci când a fost instanţiat un obiect nou)\\ \\ Atributele asociate acţiunii ''setProperty'' sunt:\\ • ''name'' – desemnează componenta Java Bean a cărei proprietate va fi stabilită; trebuie să aibă aceeaşi valoare cu a identificatorului precizat în acţiunea ''useBean'';\\ • ''property'' – indică proprietatea care se doreşte a fi stabilită; valoarea specifică faptul că parametrii cererii ale căror nume se potrivesc cu numele proprietăţilor componentei Java Beans vor fi transmişi către metodele setter respective;\\ • ''value'' – specifică valoarea care se doreşte asignată proprietăţii în cauză; dacă valoarea parametrului e ''null'' sau parametrul nu există, acţiunea este ignorată;\\ • ''param'' – numele parametrului cererii a cărei valoare trebuie să o primească proprietatea; nu se poate folosi concomitent cu ''value'' (dar cei doi parametrii pot fi omişi); <code java> | + | |
| - | <jsp:useBean id="myObject" class="myPackage.myClass"> | + | |
| - | <jsp:setProperty name="myObject" property="myProperty" value="myValue" ... /> | + | |
| - | </jsp:useBean> | + | |
| - | <jsp:useBean id="myObject" class="myPackage.myClass" /> | + | |
| - | ... | + | |
| - | <jsp:setProperty name="myObject" property="myProperty" value="myValue" ... /> | + | |
| - | </code> | | + | |
| - | | ''<jsp:forward>'' | pagina JSP curentă îşi încheie activitatea, cererea fiind transmisă către o altă resursă (pagină statică, pagină JSP sau un Java Servlet)\\ \\ Atributul asociat acţiunii forward este ''page'', prin care se indică un URL relativ al resursei spre care se doreşte a se realiza redirectarea. <code java> | + | |
| - | <jsp:forward page="myJSPPage.jsp" /> | + | |
| - | </code> | | + | |
| - | | ''<jsp:include>'' | includerea resursei specificate în momentul când pagina JSP este solicitată (spre diferenţă de directiva cu acelaşi nume, în care includerea avea loc în cadrul procesului de transformare în servlet-ul asociat)\\ \\ Atributele asociate acţiunii ''include'' sunt:\\ • ''page'' – un URL (relativ) spre pagina ce se doreşte inclusă;\\ • ''flush'' – determină dacă buffer-ul resursei va fi golit înainte de operaţia de includere. <code java> | + | |
| - | <jsp:include page="myJSPPage.jsp" flush="true" /> | + | |
| - | </code> | | + | |
| - | | ''<jsp:plugin>'' | generarea codului specific browser-ului (o etichetă ''<OBJECT>'' sau ''<EMBED>'') pentru o componentă Java (applet sau clasă Java Bean); dacă plugin-ul necesar nu este prezent, acesta este descărcat şi apoi se poate executa componenta Java; parametrii sunt transmişi prin acţiunea ''<jsp:param>'', în caz de producere a unei erori, se poate specifica un şir de caractere care să fie afişat prin acţiunea ''<jsp:fallback>'' <code java> | + | |
| - | <jsp:plugin type="applet" codebase="mydir" code="MyClass.class" width="100" height="100"> | + | |
| - | <jsp:param name="paramname" value="paramvalue" /> | + | |
| - | <jsp:fallback> | + | |
| - | Unable to load Java Plugin | + | |
| - | </jsp:fallback> | + | |
| - | </jsp:plugin> | + | |
| - | </code> | | + | |
| - | | ''<jsp:text>'' | transcrierea unui text folosind un anumit format în pagini şi documente JSP; corpul acţiunii nu poate conţine alte elemente în afară de text şi expresii EL <code java> | + | |
| - | <jsp:text>My text template</jsp:text> | + | |
| - | </code> | | + | |
| - | ==== Scripleți ==== | + | **2.** identificatorul sarcinii este transmis de la procesul de gestiune a resurselor (faţă de cazul mecanismului MapReduce clasic în care acesta era primit de la procesul de monitorizare a sarcinii); |
| - | Un scriplet conţine cod sursă Java (declaraţii de variabile şi metode, expresii) care va fi plasat în metoda de tip ''service()'' ce se va genera în servlet-ul paginii JSP respective. | + | <note tip>În terminologia mecanismului YARN, pentru o sarcină se foloseşte conceptul de aplicaţie.</note> |
| - | <code java> | + | **3.** clientul verifică specificaţiile sarcinii în privinţa locaţiei unde urmează să fie plasate rezultatele şi calculează partiţiile datelor de intrare, copiind resursele specifice sarcinii (fişierul .jar care conţine codul corespunzător sarcinii, fişierul de configurare dar şi partiţiile datelor de intrare) în sistemul distribuit de fişiere HDFS; |
| - | <% Java code fragment %> | + | |
| - | </code> | + | |
| - | <code java> | + | <note tip>Opţiunea ''yarn.app.mapreduce.am.compute-splits-in-cluster'' face ca partiţiile datelor de intrare să fie calculate în cadrul clusterului, ceea ce este util mai ales în cazul sarcinilor care au un număr mare de partiţii ale datelor de intrare.</note> |
| - | <jsp:scriptlet> | + | |
| - | Java code fragment | + | |
| - | </jsp:scriptlet> | + | |
| - | </code> | + | |
| - | Acest cod va fi executat pe server atunci când se realizează o cerere pentru pagina JSP care conţine scriplet-ul, iar rezultatul va fi plasat în răspunsul care este transmis clientului. | + | **4.** sarcina este transmisă prin apelul metodei ''submitApplication()'' în contextul procesului de gestiune a resurelor; |
| - | De regulă, utilizarea scripleţilor este de evitat în paginile JSP întrucât separarea dintre logica aplicaţiei şi prezentare nu mai este atât de evidentă, codul fiind şi mai dificil de întreţinut. Recursul la această tehnologie se face numai pentru a beneficia de facilităţile pe care le pune la dispoziţie limbajul de programare Java şi numai în situaţia în care un comportament similar nu poate fi obţinut prin folosirea EL (Expression Language) împreună cu metodele din bibliotecile JavaServer Pages Standard Tag Library. | + | **5.** când procesul de gestiune al resurselor primeşte acest apel, transmite mai departe această cerere către procesul de planificare al sarcinilor care îi alocă un container astfel încât procesul specific aplicaţiei va fi lansat (de către procesul de gestiune al resurselor) în acest context, fiind supervizat de către procesul de gestiune a nodului respectiv; |
| - | === Expresii === | + | **6.** procesul specific al aplicaţiei pentru sarcini de tip ''MapReduce'' este de fapt un program Java având clasa principală ''MRAppMaster''; acesta iniţializează sarcina în cauză creând un număr de obiecte care vor monitoriza progresul sarcinii, primind rapoarte în acest sens de la partiţiile în care va fi împărţită sarcina; |
| - | O expresie este un scriplet, cuprins între ''%%<%=%%'' şi ''%%%>%%'', evaluat în momentul în care este rulat servlet-ul şi convertit la tipul ''String'', fiind inserat în pagina JSP acolo unde este apelat, putând fi inclusă oriunde, fie că se găseşte sau nu în cadrul unei etichete HTML. Ea poate conţine orice expresie care este validă conform specificaţiei Java, însă nu este încheiată prin '';'', ca de obicei. | + | **7.** procesul specific al aplicaţiei preia partiţiile datelor de intrare determinate de client din cadrul sistemului distribuit de fişiere, creând apoi o partiţie a sarcinii (aplicaţie de tip map) pentru fiecare dintre acestea dar şi un număr de procese de tip reduce (conform proprietăţii ''mapreduce.job.reduce''); ulterior, procesul specific al aplicaţiei decide modul în care va rula partiţiile care constituie sarcina de tip MapReduce: dacă aceasta este de dimensiuni mici, partiţiile vor fi rulate în aceeaşi maşină virtuală Java ca şi el însuşi - o astfel de sarcină se spune că rulează ca o super-partiţie; o sarcină este considerată ca fiind de dimensiuni mici dacă poate fi partiţionată în mai puţin de 10 procese de tip map şi un singur proces de tip reduce, iar dimensiunea fişierului de intrare este mai mică decât un bloc HDFS; înainte ca orice partiţie a sarcinii să fie rulată, trebuie apelată metoda de configurare (o instanţă a obiectului de tip ''OutputCommitter'' corespunzător sarcinii) pentru a se crea directorul în care vor fi plasate rezultatele acesteia; |
| - | <code java> | + | <note>Decizia cu privire la rularea partițiilor sarcinii în aceeași mașină virtuală cu procesul specific al aplicației este luată ţinându-se cont de faptul că supraîncărcarea generată de alocarea şi rularea sarcinilor în alte containere depăşeşte beneficiul obţinut din rularea acestora în paralel (comparativ cu rularea lor secvenţială, în cadrul aceluiaşi nod). Un astfel de comportament este diferit de mecanismul MapReduce clasic, în care sarcinile de dimensiuni mici nu sunt rulate niciodată în contextul unui singur proces de monitorizare a partiţiilor sarcinii.</note> |
| - | <%= expression %> | + | |
| - | </code> | + | |
| - | <code java> | + | <note tip>Posibilitatea de a rula o sarcină ca o super-partiție poate fi dezactivată prin proprietatea ''mapreduce.job.ubertask.enable'' care va avea valoarea ''false''.</note> |
| - | <jsp:expression> | + | |
| - | expression | + | |
| - | </jsp:expression> | + | |
| - | </code> | + | |
| - | === Declarații === | + | <note tip>Caracteristicile unei sarcini de dimensiuni mici pot fi specificate prin intermediul proprietăţilor: |
| + | * ''mapreduce.job.ubertask.maxmaps'', respectiv ''mapreduce.job.ubertask.maxreduces'', care indică numărul de procese; | ||
| + | * ''mapreduce.job.ubertask.maxbytes'', care specifică dimensiunea unui bloc. | ||
| + | </note> | ||
| - | O **declaraţie** permite precizarea de variabile sau de metode, similar cu modul în care s-ar face într-o clasă, domeniul de vizibilitate fiind pe tot parcursul paginii respective. În servlet-ul corespunzător paginii JSP, declaraţiile vor fi incluse astfel încât să poată fi accesate din toate metodele acesteia. | + | <note>Spre diferenţă de implementarea MapReduce clasică în care metoda de configurare este apelată într-un proces special rulat de procesul de monitorizare a partiţiilor sarcinilor, în cazul implementării YARN, aceasta este apelată direct de către procesul specific al aplicaţiei.</note> |
| - | <code java> | + | **8.** în cazul în care sarcina nu întruneşte condiţiile pentru a rula ca super-partiţie, procesul specific al aplicaţiei solicită containere pentru toate procesele de tip map şi reduce din cadrul sarcinii de la modulul de gestiune a resurselor; cererile sunt incluse în cadrul rapoartelor periodice, conţinând informaţii cu privire la locaţia unde se găsesc informaţiile corespunzătoare fiecărei partiţii de tip map (nodurile şi rastelurile pe care se găsesc acestea), acestea fiind folosite de procesul de planificare în luarea deciziilor, încercându-se plasarea sarcinilor aferente în cadrul aceleiaşi maşini ca şi datele sau, în situaţia în care nu este posibil, cel puţin pe acelaşi rastel; |
| - | <%! declaration; [declaration; ]+ %> | + | |
| - | </code> | + | |
| - | <code java> | + | <note>În cadrul cererilor pentru containere se specifică de asemenea informaţii cu privire la memoria necesară; implicit, atât proceselor de tip map cât şi proceselor de tip reduce li se alocă 1024 MB de memorie, însă această valoare poate fi configurată prin intermediul proprietăţilor ''mapreduce.map.memory.mb'', respectiv ''mapreduce.reduce.memory.mb''. Modul în care este alocată memoria este diferit de mecanismul MapReduce tradițional, în care procesele de monitorizare a partiţiilor sarcinii dispuneau de un număr fix de sarcini de tip map sau de tip reduce pe care le puteau rula în paralel, stabilit la momentul configurării clusterului; fiecare sarcină putea dispune de o valoare maximă a memoriei (de asemenea având o valoare fixă pentru un cluster), existând probleme atât dacă aceasta este subutilizată (întrucât alte partiţii ale sarcinii, aflate în aşteptare, nu pot utiliza memoria nefolosită), cât şi dacă este suprautilizată (conducând la eşuarea sarcinii, de vreme ce aceasta nu dispune de memorie suficientă pentru a putea fi terminată). O astfel de problemă este rezolvată în YARN prin faptul că aplicaţiile pot solicita o anumită valoare a memoriei, ce se află între o valoare minimă şi o valoare maximă (cu condiţia de a fi un multiplu al valorii minime care poate fi alocată). Fiecare proces de planificare are valori implicite de alocare a memoriei, valoarea minimă fiind de 1024 MB (stabilită de ''yarn.scheduler.capacity.minimum-allocation-mb''), iar valorea maximă de 10240 MB (stabilită de ''yarn.scheduler.capacity.maximum-allocation-mb''). Astfel, partiţiile sarcinilor pot solicita orice alocare de memorie cuprinsă între 1GB şi 10GB, însă ca multiplu de 1GB (procesul de planificare a sarcinii va realiza rotunjiri la cea mai apropiată valoare, dacă este necesar).</note> |
| - | <jsp:declaration> | + | |
| - | declaration; | + | |
| - | [declaration; ]+ | + | |
| - | </jsp:declaration> | + | |
| - | </code> | + | |
| - | ==== Obiecte Implicite ==== | + | **9.** după ce unei partiţii a sarcinii i-a fost alocat un container de către procesul de planificare a modulului de gestiune a resurselor, procesul specific al aplicaţiei porneşte acest container, contactând procesul de gestiune a nodului; |
| - | În JSP pot fi referite **obiecte implicite**, disponibile la nivelul fiecărei pagini, putând fi utilizate fără a fi declarate în prealabil. Obiectele implicite mai sunt numite şi variabile predefinite. | + | **10.** partiţia sarcinii este executată de o aplicaţie Java a cărei clasă principală este ''YarnChild''; înainte de a rula partiţia sarcinii, va localiza resursele de care aceasta are nevoie (inclusiv configuraţia sarcinii şi arhiva .jar conţinând codul propriu-zis al sarcinii precum şi alte fişiere din cadrul zonei tampon de memorie aflate în cadrul sistemului distribuit de fişiere); |
| - | ^ Obiect Implicit ^ Tip ^ Descriere ^ | + | <note>Aplicaţia ''YarnChild'' rulează într-o maşină virtuală Java dedicată, din acelaşi motiv pentru care în cazul mecanismului MapReduce "clasic" procesele de monitorizare a partiţiei sarcinii creează noi maşini virtuale Java pentru a rula aplicaţiile corespunzătoare – izolarea codului utilizator de firele de execuţie ale sistemului de operare care rulează în fundal, având asociate sarcini care durează foarte mult. Spre diferenţă de implementarea MapReduce "clasică", YARN nu suportă reutilizarea maşinilor virtuale Java, astfel încât fiecare partiţie a sarcinii va rula într-o nouă maşină virtuală Java.</note> |
| - | | ''request'' | subclasă a ''javax.servlet.ServletRequest'' | cererea care a invocat pagina JSP | | + | |
| - | | ''response'' | subclasă a ''javax.servlet.ServletResponse'' | răspunsul pe care îl generează pagina JSP | | + | |
| - | | ''out'' | ''javax.servlet.jsp.JspWriter'' | obiect care scrie în fluxul de ieşire | | + | |
| - | | ''session'' | ''javax.servlet.http.HttpSession'' | obiect sesiune asociat cererii care a invocat pagina JSP | | + | |
| - | | ''application'' | ''javax.servlet.ServletContext'' | context pagină JSP | | + | |
| - | | ''config'' | ''javax.servlet.ServletConfig'' | informaţii de configurare referitoare la servlet | | + | |
| - | | ''pageContext'' | ''javax.servlet.jsp.PageContext'' | contextul paginii JSP | | + | |
| - | | ''page''' | ''java.lang.Object'' | referinţă către pagina JSP prin care poate fi accesat servlet-ul asociat | | + | |
| - | | ''exception'' | ''java.lang.Throwable'' | acces la detalii cu privire la eroare, în paginile de tratare a excepţiei | | + | |
| - | Astfel de obiecte vor fi disponibile **doar** în cadrul metodei ''_jspService()'', astfel încât referirea acestora în cadrul unei declaraţii JSP nu are sens. | + | **11.** containerul rulează partiţia sarcinii (de tip map sau reduce). |
| - | Este important ca programatorii să cunoască denumirile acestor obiecte implicite precum şi domeniul lor de vizibilitate, astfel încât să le poată accesa funcţionalitatea în acelaşi mod în care ar fi făcut-o în cadrul clasei Java Servlet corespunzătoare, în care acesteau erau declarate fie ca parametrii ai metodelor respective, fie se putea obţine o instanţă a lor pornind de la obiectele cerere şi răspuns. Prin acestea, tehnologiile Java Servlet şi JavaServer Pages sunt echivalente, oferind aceeaşi funcţionalitate şi aceeaşi uşurinţă în utilizare. | + | În cazul Yarn, partiţia sarcinii îşi raportează starea şi progresul (inclusiv valoarea contoarelor) la procesul specific al aplicaţiei, care dispune astfel de o viziune de asamblu a sarcinii. |
| - | === Obiectul implicit request === | + | <note>Astfel de rapoarte sunt transmise la fiecare trei secunde, prin intermediul interfeţei ombilicale. Procesul este mai simplu decât în cazul implementării MapReduce "clasice" în care actualizările cu privire la progres sunt transmise prin procesele de monitorizare a partiţiilor sarcinilor către procesul de monitorizare a sarcinii care deţine viziunea de ansamblu asupra acesteia.</note> |
| - | Un obiect ''request'' este creat de fiecare dată când un client solicită o pagină JSP, acesta oferind metode spre a obţine informaţii din antetele HTTP (''Accept'', ''Accept-Charset'', ''Accept-Encoding'', ''Accept-Language'', ''Authorization'', ''Cache-Control'', ''Connection'', ''Content-Length'', ''Cookie'', ''Host'', ''If-Modified-Since'', ''If-Unmodified-Since'', ''Referer'', ''User-Agent''). Fiind derivat din ''javax.servlet.http.HttpServletRequest'', metodele pe care le pune la dispoziţia programatorilor acest obiect sunt aceleaşi ca în cazul utilizării ca parametru al metodei ''service()'' din cadrul Java Servlets. | + | La rândul său, procesul specific al aplicației e interogat de client la fiecare secundă (sau în intervalul stabilit de proprietatea ''mapreduce.client.progressmonitor.pollinterval''). Totodată, interfaţa grafică a procesului de gestiune a resurselor afişează toate aplicaţiile care se află în execuţie, împreună cu legături către interfeţele grafice ale proceselor specifice aplicaţiilor respective, care conţin detalii despre sarcina MapReduce, inclusiv progresul acesteia. |
| - | Astfel, cele mai frecvent folosite metode sunt: | + | <note>În cazul implementării MapReduce "clasice", lista sarcinilor aflate în execuţie precum şi progresul lor putea fi consultat prin intermediul interfeţei grafice puse la dispoziţie de procesul de monitorizare a sarcinilor.</note> |
| - | * ''Object getAttribute(String)'' – întoarce valoarea obiectului identificat prin numele său (transmis ca parametru) – convertit la ''Object'' sau ''null'' dacă obiectul solicitat nu există; | + | |
| - | * ''Enumeration getAttributeNames()'' – întoarce o enumerare conţinând numele tuturor atributelor disponibile în cadrul cererii; | + | |
| - | * ''String getAuthType()'' – întoarce numele mecanismului de autentificare utilizat pentru securizarea servlet-ului sau ''null'' dacă nu se foloseşte un astfel de mecanism; | + | |
| - | * ''String getCharacterEncoding()'' – întoarce denumirea schemei de codificare utilizată pentru corpul cererii; | + | |
| - | * ''int getContentLength()'' – întoarce dimensiunea (în octeţi) a corpului cererii disponibilă în fluxul de intrare, sau -1 dacă această valoare nu e cunoscută; | + | |
| - | * ''String getContentType()'' – întoarce tipul MIME al corpului cererii sau null dacă tipul nu este cunoscut; | + | |
| - | * ''String getContextPath()'' – întoarce porţiunea din URI-ul cererii care indică contextul acesteia; | + | |
| - | * ''Cookie[] getCookies()'' – întoarce toate obiectele ''Cookie'' care au fost transmise de client împreună cu cererea sa; | + | |
| - | * ''String getHeader(String)'' – întoarce valoarea antetului specificat din cadrul cererii sub formă de şir de caractere; | + | |
| - | * ''Enumeration getHeaderNames()'' – întoarce o enumerare ce cuprinde numele tuturor antetelor conţinute de cerere; | + | |
| - | * ''int getIntHeader(String)'' – întoarce valoarea antetului specificat din cadrul cererii sub formă de întreg; | + | |
| - | * ''ServletInputStream getInputStream()'' – întoarce corpul cererii (ca date binare) folosind un ''ServletInputStream''; | + | |
| - | * ''Locale getLocale()'' – întoarce localizarea preferată a conţinutului, în funcţie de valoarea conţinută de antetul ''Accept-Language''; | + | |
| - | * ''String getMethod()'' – întoarce numele metodei HTTP prin care a fost transmisă cererea (''GET'', ''POST'', ''PUT'', ''OPTIONS'', ''TRACE'', ''DELETE''); | + | |
| - | * ''String getParameter(String)'' – întoarce valoarea parametrului specificat (prin nume) în cadrul cererii ca şir de caractere sau ''null'' dacă nu există; | + | |
| - | * ''Enumeration getParameterNames()'' – întoarce o enumerare incluzând denumirile tuturor parametrilor din cadrul cererii; | + | |
| - | * ''String[] getParameterValues(String)'' – întoarce un vector de obiecte de tip şir de caractere conţinând toate valoarile pe care le deţine parametrul identificat (prin nume) în cadrul cererii sau null dacă nu există; | + | |
| - | * ''String getPathInfo()'' – întoarce informaţii suplimentare referitoare la cale asociate cu URL-ul transmis împreună cu cererea; | + | |
| - | * ''String getProtocol()'' – întoarce numele şi versiunea protocolului prin care a fost transmisă cererea; | + | |
| - | * ''String getQueryString()'' – întoarce un şir de caractere reprezentând interogarea cuprinsă în URL după cale; | + | |
| - | * ''String getRemoteAddr()'' – întoarce adresa IP (Internet Protocol) a clientului care a transmis cererea; | + | |
| - | * ''String getRemoteHost()'' – întoarce numele complet al maşinii de pe care clientul a transmis cererea; | + | |
| - | * ''String getRemoteUser()'' – întoarce numele utilizatorului autentificat care a transmis cererea sau ''null'' dacă utilizatorul nu este autentificat; | + | |
| - | * ''String getRequestURI()'' – întoarce partea din URL-ul asociat cererii începând de la numele protocolului până la şirul ce reprezintă interogarea din cadrul cererii HTTP; | + | |
| - | * ''String getRequestedSessionId()'' – întoarce identificatorul sesiunii specificat de client în cerere; | + | |
| - | * ''int getServerPort()'' – întoarce portul pe care a fost primită cererea; | + | |
| - | * ''String getServletPath()'' – întoarce partea din URL-ul asociat cererii care invocă pagina JSP; | + | |
| - | * ''HttpSession getSession([boolean])'' – întoarce sesiunea asociată cererii sau, dacă aceasta nu există (iar parametrul metodei, în caz că este utilizat, are valoarea ''true''), asociază cererii o sesiune nouă; | + | |
| - | * ''boolean isSecure()'' – indică dacă cererea a fost transmisă folosind un canal sigur (cum este HTTPS). | + | |
| - | O listă cu antetele HTTP transmise prin intermediul cererii poate fi obţinută astfel: | + | În acelaşi mod în care clientul interoghează procesul specific al aplicaţiei pentru a determina progresul acesteia, acesta verifică (la fiecare cinci secunde) dacă sarcina a fost terminată, printr-un apel al metodei ''waitForCompletion()'' pe obiectul de tip ''Job''. Există posibilitatea ca notificarea cu privire la terminare să se realizeze prin apeluri inverse folosind protocolul HTTP, însă acestea sunt iniţiate de procesul specific al aplicaţiei. |
| - | <code java> | + | |
| - | ... | + | |
| - | <table> | + | |
| - | <tr><th>Header Name</th><th>Header Value</th></tr> | + | |
| - | <% | + | |
| - | Enumeration headers = request.getHeaderNames(); | + | |
| - | while (headers.hasMoreElements()) { | + | |
| - | String headerName = (String)headers.nextElement(); | + | |
| - | String headerValue = request.getHeader(headerName); | + | |
| - | %> | + | |
| - | <tr><td><%= headerName %></td><td><%= headerValue %></td></tr> | + | |
| - | <% | + | |
| - | } | + | |
| - | %> | + | |
| - | </table> | + | |
| - | ... | + | |
| - | </code> | + | |
| - | === Obiectul implicit response === | + | <note tip>Intervalul la care este verificată terminarea sarcinii poate fi stabilit prin intermediul proprietăţii ''mapreduce.client.completion.pollinterval'', în fişierul de configurare.</note> |
| - | Un obiect ''response'' este de asemenea creat de fiecare dată de către server, conţinând: | + | La terminarea sarcinii, procesul specific al aplicaţiei şi containerele în care sunt rulate partiţiile acesteia eliberează resursele utilizate, printr-un apel al metodei corespunzătoare din cadrul obiectului ''OutputCommitter''. Informaţiile cu privire la sarcină sunt arhivate de către serverul ce reţine istoricul acestora, pentru a permite interogări ulterioare din partea clienţilor, în caz de nevoie. |
| - | * starea (numele protocolului și versiunea acestuia, codul reprezentând starea și un mesaj de dimensiuni mici care reprezintă o descriere a stării); | + | |
| - | * antetele HTTP (''Allow'', ''Cache-Control'', ''Connection'', ''Content-Disposition'', ''Content-Encoding'', ''Content-Language'', ''Content-Length'', ''Content-Type'', ''Expires'', ''Last-Modified'', ''Location'', ''Refresh'', ''Retry-After'', ''Set-Cookie''); | + | |
| - | * documentul reprezentând pagina Internet care va fi afişată în browser. | + | |
| - | Fiind derivat din ''javax.servlet.http.HttpServletResponse'', metodele pe care le pune la dispoziţia programatorilor acest obiect sunt aceleaşi ca în cazul utilizării ca parametru al metodei ''service()'' din cadrul Java Servlets. | + | ==== Tratarea Erorilor ==== |
| - | Astfel, cele mai frecvent folosite metode sunt: | + | Unul dintre principalele avantaje ale Hadoop constă în posibilitatea de **recuperare din eroare**, situaţie întâlnită cu o frecvenţă destul de mare şi datorată greşelilor din codul sursă al utilizatorilor, blocajelor în cadrul proceselor şi defectării maşinilor pe care rulează acestea. |
| - | * ''void addCookie(Cookie)'' – adaugă la răspuns obiectul ''Cookie'' specificat; | + | În cazul **implementării MapReduce "clasice"** defectele se pot produce la nivelul: |
| - | * ''void addDateHeader(String, long)'' – adaugă la răspuns un antet reprezentând o dată calendaristică, având numele şi valoarea specificate ca parametrii ai metodei; | + | - unei partiţii a sarcinii; |
| - | * ''void addHeader(String, String)'' – adaugă la răspuns un antet având numele şi valoarea (şir de caractere) specificate ca parametrii ai metodei; | + | - procesului ce o monitorizează; |
| - | * ''void addIntHeader(String, int)'' – adaugă la răspuns un antet având numele şi valoarea (număr întreg) specificate ca parametrii ai metodei; | + | - procesului care monitorizează sarcina la nivel global. |
| - | * ''boolean containsHeader(String)'' – verifică dacă un antet (indicat prin nume, dat ca parametru) a fost specificat în cadrul răspunsului; | + | |
| - | * ''String encodeRedirectURL(String)'' – codifică URL-ul specificat ca parametru pentru a fi utilizat de metoda ''sendRedirect'' sau dacă nu este necesară codificarea, îl lasă neschimbat; | + | |
| - | * ''String encodeURL(String)'' – codifică URL-ul specificat ca parametru (incluzând identificatorul sesiunii în cadrul său) sau dacă nu este necesară codificarea, îl lasă neschimbat; | + | |
| - | * ''void flushBuffer()'' – forţează afişarea conţinutului zonei de memorie tampon în browser; | + | |
| - | * ''boolean isCommitted()'' – verifică dacă răspunsul a fost transmis către client; | + | |
| - | * ''void reset()'' – elimină toate informaţiile din zona de memorie tampon, inclusiv coduri de statut şi antete; | + | |
| - | * ''void resetBuffer()''– elimină conţinutul zonei de memorie tampon, menţinând însă codurile de statut şi antetele intacte; | + | |
| - | * ''void sendError(int[, String])'' – transmite un răspuns către client reprezentând o eroare indicată prin codul său (şi eventual printr-un mesaj) eliminând şi conţinutul zonei de memorie tampon; | + | |
| - | * ''void sendRedirect(String)'' – transmite clientului un răspuns temporar de redirectare către o locaţie indicată prin URL-ul dat ca parametru; | + | |
| - | * ''void setBufferSize(int)'' – stabileşte dimensiunea preferată (în octeţi) pentru zona de memorie ce reţine conţinutul răspunsului; | + | |
| - | * ''void setCharacterEncoding(String)'' – stabileşte mecanismul de codificare a caracterelor (setul de caractere ''MIME'') corespunzând răspunsul transmis către client; | + | |
| - | * ''void setContentLength(int)'' – stabileşte dimensiunea conţinutului transmis către client ca răspuns (pentru protocolul HTTP se stabilește valoarea pe care o are antetul ''Content-Length''); | + | |
| - | * ''void setContentType(String)'' – stabileşte tipul de conţinut corespunzător răspunsului transmis către client, dacă această operaţie nu s-a produs deja; | + | |
| - | * ''void setDateHeader(String, long)'' – stabileşte un antet reprezentând o dată calendaristică având numele şi valoarea specificate ca parametrii ai metodei; | + | |
| - | * ''void setHeader(String, String)'' – stabileşte un antet cu numele şi valoarea (şir de caractere) specificate ca parametrii ai metodei; | + | |
| - | * ''void setIntHeader(String, int)'' – stabileşte un antet cu numele şi valoarea (număr întreg) specificate ca parametrii ai metodei; | + | |
| - | * ''void setLocale(Locale)'' – stabileşte localizarea răspunsului, dacă acesta nu a fost transmis către client; | + | |
| - | * ''void setStatus(int)'' – stabileşte codul de stare pentru răspuns. | + | |
| - | Stabilirea unor valori corespunzătoare antetelor HTTP din răspuns poate fi realizat astfel: | + | **a)** Cel mai frecvent, **erorile de la nivelul unei partiţii a sarcinii** se datorează greşelilor din codul sursă, generându-se o excepţie la rulare care este raportată procesului care o monitorizează, aceasta fiind înregistrată şi în cadrul jurnalelor. Procesul de monitorizare a partiţiei sarcinii marchează această încercare ca fiind eşuată, eliberând poziţia ocupată de aceasta pentru ca în cadrul acesteia să fie rulată o altă partiţie a sarcinii. În cazul în care maşina virtuală Java îşi încetează activitatea brusc (datorită unor probleme ale acesteia pentru setul de date al partiţiei respective a sarcinii), procesul de monitorizare va detecta faptul că partiţia sarcinii nu mai rulează şi marchează încercarea de execuţie ca fiind eşuată. În situaţia în care partiţiile sarcinilor sunt suspendate în execuţie, procesul de monitorizare detectează că nu a primit o actualizare a progresului pentru o perioadă de timp mai îndelungată, declanşând procesul de marcare a încercării de rulare a partiţiei sarcinii ca eşuată şi determinând închiderea maşinii virtuale Java care o rula. |
| - | <code java> | + | <note>Perioada de timp după care partiţiile sarcinilor sunt considerate eşuate e de obicei 10 minute, putând fi configurată (pentru fiecare sarcină în parte sau pentru fiecare cluster în parte) prin intermediul proprietăţii ''mapred.task.timeout'' a cărei valoare este exprimată în milisecunde. Indicarea unei valori nule pentru această proprietate dezactivează mecanismul de expirare, astfel că sarcinile a căror execuţie durează mai mult nu vor fi marcate niciodată ca fiind eşuate. Într-un astfel de caz, o partiţie a sarcinii suspendată în execuţie nu îşi va elibera niciodată poziţia, existând posibilitatea de a se înregistra încetinerea globală a cluster-ului. De aceea, acest comportament trebuie evitat, asigurându-se că partiţiile sarcinilor îşi raportează progresul.</note> |
| - | ... | + | |
| - | <% | + | |
| - | response.setDateHeader("Last-Modified",System.currentTimeMillis()); | + | |
| - | response.setHeader("Content-Type","text/html"); | + | |
| - | response.setIntHeader("Refresh", 60); | + | |
| - | %> | + | |
| - | ... | + | |
| - | </code> | + | |
| - | În exemplu, se stabileşte valoarea antetului ''Last-Modified'' ca fiind momentul curent, tipul conţinutului pe care îl are transmis către client (indicat de antetul ''Content-Type'') ca fiind un document HTML, forţând reîncărcarea paginii în mod automat (prin antetul ''Refresh'') la fiecare 60 de secunde. | + | Când procesul de monitorizare a partiţiei sarcinii este notificat de eşecul unei încercări (prin intermediul rapoartelor periodice), va replanifica execuţia ei, încercându-se să se evite situaţia în care rularea va fi realizată în contexul unui proces de monitorizare în care a eşuat anterior. Mai mult, în cazul în care o partiţie a sarcinii eşuează de mai mult de patru ori, ea nu va fi reexecutată. Implicit, dacă orice partiţie a sarcinii eşuează de mai mult de patru ori (sau de valoarea maximă configurată pentru numărul de încercări), întreaga sarcină va eşua. Pentru unele aplicaţii, nu este de dorit ca sacina să eşueze din cauza unor partiţii ale sale care au înregistrat erori, întrucât este posibil ca rezultatele furnizate de partiţiile terminate cu succes să poată fi folosite, motiv pentru care se poate configura proporția de partiţii ale sarcinii care pot să eşueze fără a declanşa eşuarea sarcinii. O partiţie a sarcinii poate fi oprită în mod forţat, însă astfel de încercări nu sunt contorizate în numărul maxim de lansare în execuţie, de vreme ce eroarea nu s-a produs din cauza partiţiei sarcinii. Şi utilizatorii au posibilitatea de a termina încercări ale partiţiilor sarcinilor (sau chiar sarcina în totalitate) folosind interfaţa grafică sau linia de comandă. |
| - | === Obiectul implicit out === | + | <note tip>Numărul maxim de încercări de a executa o sarcină este dat de proprietatea ''mapred.map.max.attempts'' pentru partiţii ale sarcinii de tip map, respectiv de proprietatea ''mapred.reduce.max.attempts'' pentru partiţii ale sarcinii de tip reduce.</note> |
| - | Obiectul ''out'' este echivalentul obiectului ''PrintWriter'' din Java Servlets, fiind utilizat la afişarea conţinutului dinamic în cadrul paginii JSP, atunci când aceasta nu se poate face prin intermediul unor etichete HTML (eventual combinate cu expresii JSP). | + | <note tip>Procentul de partiţii de tip map respectiv de tip reduce care pot eșua sunt controlate independent, folosind proprietăţile ''mapred.max.map.failures.percent'', respectiv ''mapred.max.reduce.failures.percent''.</note> |
| - | <note tip>Spre diferenţă de obiectul ''PrintWriter'', un obiect ''JspWriter'' va genera excepţii de tipul ''java.io.IOException''. De asemenea, pune la dispoziţia utilizatorilor metode suplimentare pentru gestiunea zonei de memorie tampon.</note> | + | <note>O încercare a partiţiei sarcinii poate fi oprită datorită faptului că este un duplicat speculativ sau datorită faptului că procesul său de monitorizare a eşuat, astfel încât procesul de monitorizare a sarcinilor va termina în mod forţat toate partiţiile ce rulează în contextul său.</note> |
| - | Iniţial, obiectul este instanţiat diferit în funcţie de activarea mecanismului de utilizare a unei zone de memorie tampon (aşa cum este specificat de atributul ''buffered'' al directivei ''page''). Metodele ''print'' şi ''println'' pot fi utilizate spre a afişa conţinutul unor obiecte cu tipurile ''boolean'', ''char'', ''String'', ''int'', ''long'', ''float'', ''double'', ''Object'' şi altele. De asemenea, metoda ''flush()'' este folosită pentru a transmite conţinutul zonei de memorie tampon către client, golind conţinutul fluxului de ieşire. | + | **b)** **Erorile produse la nivelul procesului de monitorizare a partiţiei sarcinii** reprezintă altă situaţie care determină eşecul sarcinii. Într-un astfel de caz, procesul de monitorizare a sarcinii va detecta că acesta nu mai funcţionează dacă mesajele periodice întârzie de a fi transmise mai mult de 10 minute, eliminându-l din lista sa, planificând partiţiile sarcinilor care au rulat pe acesta pe alte procese de monitorizare, chiar în situaţia în care au fost terminate cu succes, dacă aparţin unor sarcini incomplete (întrucât valorile de ieşire intermediare se găsesc în sistemul de fişiere al procesului de monitorizare defect astfel încât pot să nu fie accesibile). Un proces de monitorizare a partiţiei sarcinii poate fi inclus de asemenea într-o "listă neagră" de către procesul de urmărire a sarcinii, dacă mai mult de patru partiţii din cadrul aceleiaşi sarcini au eşuat rulând pe acesta. Unui astfel de proces nu i se vor mai repartiza partiţii ale sarcinii, continuând însă să comunice cu procesul de monitorizare a sarcinii. Există implementat un mecanism de expirare a erorilor (cu o rată de una pe zi), astfel încât procesele de monitorizare a partiţiei sarcinii pot fi scoase din cadrul "listei negre" doar prin faptul că rulează în continuare. În situaţia în care erorile au fost produse de o situaţie care poate fi remediată, acesta va fi eliminat din "lista neagră" după ce este repornit şi reintegrat în cadrul clusterului. |
| - | === Obiectul implicit session === | + | <note>Defecţiunile ce se pot înregistra la un proces de monitorizare a partiției sarcinii vizează fie terminarea (sau blocarea sa), fie funcţionarea sa foarte lentă, ceea ce determină ca mesajele periodice să nu mai fie transmise deloc sau să fie transmise mai rar.</note> |
| - | Obiectul ''session'' este creat în mod automat (nu mai este necesară iniţializarea sau obţinerea dintr-un obiect de tip ''request'' prin metoda ''getSession()'') pentru fiecare client ce accesează pagina în cauză. Dezactivarea acestui tip de comportament poate fi realizată doar prin intermediul atributului cu acelaşi nume din directiva ''page''. | + | <note tip>Valoarea ce indică perioada de timp maximă în care se așteaptă mesajele periodice înainte ca procesul de monitorizare a sarcinii să constate o disfuncționalitate la nivelul procesului de monitorizare a pertiției sarcinii poate fi configurată prin intermediul proprietăţii ''mapred.task.tracker.expiry.interval'', care specifică o valoare exprimată în milisecunde.</note> |
| - | Metodele pe care le pune la dispoziţie acest obiect sunt cele specificate de interfaţa ''javax.servlet.http.HttpSession'': | + | <note tip>Valoarea care indică numărul de partiții al unei sarcini care au înregistrat erori în cadrul aceluiași proces de monitorizare (fiind incluse pe "lista neagră") poate fi modificată prin intermediul proprietăţii ''mapred.max.tracker.blacklists'', acesta fiind un prag mai mare decât media de erori înregistrate de procesele de monitorizare pentru partiţiile sarcinii din cadrul unui cluster.</note> |
| - | * ''Object getAttribute(String)'' – întoarce obiectul care este asociat numelui transmis ca parametru al metodei în cadrul sesiunii sau ''null'' dacă nu există nici un obiect asociat; | + | |
| - | * ''Enumeration getAtrributeNames()'' – întoarce o enumerare ce conţine denumirile tuturor obiectelor asociate sesiunii; | + | |
| - | * ''long getCreationTime()'' – întoarce momentul la care a fost creată sesiunea, exprimată în milisecunde raportate la data 1 ianuarie 1970 (GMT); | + | |
| - | * ''String getId()'' – întoarce un şir de caractere care identifică în mod unic sesiunea; | + | |
| - | * ''long getLastAccessedTime()'' – întoarce momentul la care clientul a transmis ultima dată o cerere asociată sesiunii, exprimată în milisecunde raportate la data 1 ianuarie 1970 (GMT); | + | |
| - | * ''int getMaxInactiveInterval()'' – întoarce perioada de timp maximă, exprimată în secunde, în care containerul servletului va menţine sesiunea, între diferite accesări provenite de la clienţi; | + | |
| - | * ''void invalidate()'' – invalidează sesiunea, eliminând orice asociere a unui obiect la aceasta; | + | |
| - | * ''boolean isNew()'' – stabileşte dacă clientul nu are cunoştinţă de sesiune sau dacă alege să nu participe la aceasta; | + | |
| - | * ''void removeAttribute(String)'' – elimină asocierea obiectului identificat prin numele transmis ca parametru al metodei la sesiune; | + | |
| - | * ''void setAttribute(String, Object)'' – asociază la sesiune un obiect ale cărui nume şi valoare sunt specificate ca parametrii ai metodei; | + | |
| - | * ''void setMaxInactiveInterval(int)'' – stabileşte perioada de timp maximă, exprimată în secunde, care se poate scurge între diferite accesări provenite de la clienţi până când sesiunea va fi invalidată de containerul servletului; valoarea poate fi specificată şi în fişierul de configurare ''web.xml'': <code xml> | + | |
| - | <session-config> | + | |
| - | <session-timeout>60</session-timeout> | + | |
| - | </session-config> | + | |
| - | </code> Această valoare va suprascrie valoarea implicită specificată de serverul Apache Tomcat 8.x, care este de 30 de minute. În fişierul de configurare ''web.xml'' timpul de expirare va fi specificat, spre diferenţă de metoda ''setMaxInactiveInterval()'' nu în secunde, ci în minute. | + | |
| - | În afară de sesiuni, pentru gestiunea comunicaţiei dintre client şi server, pot fi utilizate **cookie-uri**, fişiere text reţinute pe maşina client (dacă aceasta permite stocarea lor) şi citite de server pentru a identifica în mod unic conexiunea respectivă. Acestea sunt transmise de regulă prin antete HTTP, câmpul ''Set-Cookie'' reţinând perechi de tipul cheie-valoare pentru atributele ''name'', ''expires'', ''path'' şi ''domain''. De fiecare dată când clientul va accesa o pagină care corespunde cu valorile reţinute de atributele ''path'' şi ''domain'', în cazul în care nu a fost depăşită perioada specificată de atributul ''expires'', cookie-ul va fi inclus în antetele HTTP transmise către server. | + | **c)** Producerea unei **erori la nivelul procesului de monitorizare a sarcinii** reprezintă situaţia cea mai gravă care poate conduce la eşecul rulării sarcinii. Fiind singurul punct vulnerabil din cadrul arhitecturii, Hadoop nu dispune de un mecanism de recuperare dintr-o astfel de situaţie, astfel încât toate sarcinile care rulează la un moment dat vor eşua. După repornirea procesului de monitorizare a sarcinii, toate sarcinile aflate în execuţie la momentul producerii erorii respective vor trebui să fie retransmise. |
| - | Metodele pe care le pune la dispoziţie clasa ''Cookie'' sunt: | + | <note>Probabilitatea producerii unei astfel de situații este destul de mică (de vreme ce probabilitatea defectării unei anumite maşini din cadrul clusterului este mică), această problemă fiind rezolvată de implementarea YARN, al cărei scop a fost eliminarea punctelor de vulnerabilitate identificate în mecanismul MapReduce.</note> |
| - | * ''String getComment()'' – întoarce comentariul care descrie scopul obiectului cookie sau ''null'' dacă nu există nici un comentariu asociat; | + | |
| - | * ''String getDomain()'' – returnează domeniul pentru care se aplică obiectul cookie; | + | |
| - | * ''int getMaxAge()'' – pune la dispoziţie durata de viaţă (exprimată în secunde) pentru obiectul cookie; valoarea -1 exprimă faptul că obiectul cookie va persista până când se va părăsi browserul; | + | |
| - | * ''String getName()'' – obţine denumirea obiectului cookie; danumirea unui obiect cookie nu mai poate fi modificată după crearea sa; | + | |
| - | * ''String getPath()'' – reflectă calea pentru care se aplică obiectul cookie; | + | |
| - | * ''String getValue()'' – redă valoarea asociată obiectului cookie; | + | |
| - | * ''void setComment(String)'' – fixează un comentariu care indică scopul obiectului cookie, acesta fiind util în situaţia în care browserul îl afişează utilizatorului pentru a lua o decizie în privinţa sa; | + | |
| - | * ''void setDomain(String)'' – indică domeniul pentru care se aplică obiectul cookie; | + | |
| - | * ''void setMaxAge(int)'' – stabileşte timpul (exprimat în secunde) care ar trebui să se scurgă înainte de expirarea obiectului cookie; în cazul în care atributul nu este precizat, obiectul cookie va exista doar pe parcursul sesiunii curente; | + | |
| - | * ''void setPath(String)'' – setează calea pentru care se aplică obiectul cookie; dacă acest atribut nu este specificat, obiectul cookie este transmis pentru toate URL-urile care se găsesc în acelaşi director sau subdirector cu pagina curentă; | + | |
| - | * ''void setSecure(boolean)'' – determină dacă obiectul cookie ar trebui transmis numai prin legături criptate; | + | |
| - | * ''void setValue(String)'' – precizează valoarea asociată cu obiectul cookie. | + | |
| - | Utilizarea unui obiect ''Cookie'' presupune crearea sa, precizarea duratei (maxime) de viaţă şi transmiterea sa în cadrul obiectului ''response'' la client prin metoda ''addCookie()''. | + | <note tip>Există şi posibilitatea de a încerca recuperarea sarcinilor care rulau la momentul opririi procesului de monitorizare (prin proprietatea ''mapred.jobtracker.restart.recover''), însă au fost raportate anumite probleme în funcţionarea sa, astfel încât această opţiune nu ar trebui folosită.</note> |
| - | Crearea unui obiect de tip Cookie implică specificarea unei denumiri şi a valorii asociate. Nici denumirea şi nici valoarea asociată nu trebuie să conţină în cadrul lor caracterele ''['', '']'', ''('', '')'', ''='', '','', ''"'', ''/'', ''?'', ''@'', '':'', '';''. | + | În cazul **mecanismului YARN**, la rularea unor aplicaţii MapReduce se pot produce erori la nivelul: |
| + | - partiţiei sarcinii; | ||
| + | - procesului specific al aplicaţiei; | ||
| + | - procesului de gestiune a nodului; | ||
| + | - procesului de gestiune al resurselor. | ||
| - | Totodată, ştergerea presupune stabilirea duratei (maxime) de viaţă nule înainte de a fi inclus în antetele HTTP către browserul ce se ocupă cu reţinerea sa. | + | **a)** **Erorile produse pentru o partiţie a sarcinii** sunt tratate într-un mod asemănător cu cazul MapReduce "clasic". Excepţiile la rulare precum şi terminarea bruscă a maşinii virtuale Java sunt propagate până la procesul specific al aplicaţiei, şi încercarea de rulare a partiţiei sarcinii este marcată ca fiind eşuată. De asemenea, procesele a căror execuţie durează prea mult (care sunt blocate) sunt detectate prin absenţa transmiterii unui mesaj periodic (ping) prin interfaţa ombilicală. Se pot folosi aceleaşi proprietăţi de configurare pentru a indica eşecul definitiv al unei partiţii a sarcinii sau al întregii aplicaţii. |
| - | === Obiectul implicit application === | + | <note tip>Perioada la care este aşteptat un mesaj periodic (pinb) poate fi configurat prin intermediul proprietăţii ''mapreduce.task.timeout''.</note> |
| - | Obiectul ''application'' este de fapt un wrapper pentru obiectul ''ServletContext'' al servletului asociat paginii JSP, fiind o reprezentare a acesteia pe parcursul întregului său ciclu de viaţă, fiind iniţializat în metoda ''_jspInit()'' şi distrus în metoda ''_jspDestroy()''. Este folosit împreună cu metodele ''getAttribute(String)'' şi ''setAttribute(String, Object)'' atunci când se doreşte ca proprietăţile respective să aibă un domeniu de vizibilitate corespunzător întregii aplicaţii, adică tuturor paginilor care o alcătuiesc. O funcţionalitate ce se pretează folosirii acestui obiect este determinarea numărului de accesări al aplicaţiei respective. Într-o astfel de situaţie, pentru a se evita situaţia pierderii datelor în cazul repornirii aplicaţiei (serverului web care o găzduieşte), trebuie asigurată şi persistenţa acestora prin stocarea lor într-o bază de date. | + | <note tip>O partiţie a sarcinii este considerată eşuată după ce au fost raportate erori pentru un anumit număr de încercări: ''mapreduce.map.maxattempts'' pentru sarcini de tip map şi ''mapreduce.reduce.maxattempts'' pentru sarcini de tip reduce. De asemenea, o sarcină în ansamblu va fi marcată ca fiind eşuată în situaţia în care eşuează mai mult de un anumit procent de partiţii ale sale de tip map (indicat de ''mapreduce.map.failures.maxpercent'') sau un anumit procent de partiţii ale sale de tip reduce (indicat de ''mapreduce.reduce.failures.maxpercent'').</note> |
| - | === Obiectul implicit config === | + | **b)** Şi în cazul **aplicaţiilor care eşuează**, sunt realizate mai multe încercări. Acestea sunt detectate în situaţia în care procesul specific al aplicaţiei nu mai transmite mesaje periodice către procesul de gestiune a resurselor, situaţie în care va fi creată o nouă instanţă a procesului specific al aplicaţiei, însă într-un alt container, supervizat de un proces de gestiune a nodului. Partiţiile sarcinii care au fost deja rulate pot fi recuperate în acest caz, astfel că nu este necesară execuţia lor din nou. Întrucât clientul solicită procesului specific al aplicaţiei rapoarte cu privire la progresul înregistrat, în situaţia în care acesta eşuează şi nu mai transmite nici o informaţie, clientul va interoga procesul de gestiune al resurselor pentru a primi adresa noii instanţe a procesului specific al aplicaţiei (aşa cum îi fusese transmisă şi la iniţializarea sarcinii, când a fost transmisă). |
| - | Obiectul ''config'' este şi el un wrapper pentru obiectul ''ServletConfig'', permiţând programatorilor să acceseze parametrii de iniţializare ai motorului Java Servlet sau JSP, obţinuţi prin metodele ''getInitParameter(String)'', respectiv ''getInitParameterNames()''. Numele servletului asociat, aşa cum este specificat prin elementul ''<servlet-name>'' din fişierul de configurare ''web.xml'', poate fi accesat prin metoda ''getServletName()''. | + | <note tip>O aplicaţie este marcată ca fiind eşuată dacă se înregistrează erori o singură dată, însă această valoare poate fi modificată prin proprietatea ''yarn.resourcemanager.am.max-retries''.</note> |
| - | === Obiectum implicit pageContext === | + | <note tip>Recuperarea din eroare nu este activată în mod implicit (aceasta se face prin intermediul proprietăţii ''yarn.app.mapreduce.am.job.recovery.enable''), astfel că procesul specific al aplicaţiei va rula din nou toate partiţiile sarcinilor, indiferent de starea anterioară.</note> |
| - | Obiectul ''pageContext'' este folosit ca o reprezentare a întregii pagini JSP, având scopul de a accesa informaţia cu privire la aceasta, evitând majoritatea detaliilor cu privire la implementare. Acest obiect reţine referinţe către obiectele ''request'' şi ''response'', iar obiectele ''config'', ''session'' şi ''out'' pot fi obţinute prin atribute ale sale. De asemenea, el conţine şi informaţii cu privire la directivele transmise paginii JSP. În cadrul său sunt definite mai multe câmpuri, printre care şi domeniile de vizibilitate ''PAGE_SCOPE'', ''REQUEST_SCOPE'', ''SESSION_SCOPE'', ''APPLICATION_SCOPE''. Implementează mai multe metode, majoritatea fiind moştenite din clasa ''javax.servlet.jsp.JspContext''. Se pot realiza operaţii pe atribute (stabilire, obţinere sau ştergere) de fiecare dată specificându-se şi domeniul său de vizibilitate. | + | **c)** Dacă se înregistrează **o eroare la nivelul procesului de gestiune a nodului**, acesta va înceta să mai transmită mesaje periodice către procesul de gestiune a resurselor, care îl va elimina din lista sa de noduri disponibile. Orice partiţie a sarcinii sau proces specific al aplicaţiei care rulează pe nodul marcat ca fiind eşuat vor fi recuperate. Procesele de gestiune a nodurilor pot fi incluse pe "lista neagră" în situaţia în care numărul de erori al aplicaţiilor rulate în contextul său este mare. Acestă operaţie este realizată de procesul specific al aplicaţiei (care va planifica partiţiile sarcinilor pe alte noduri) în cazul în care este depăşit un anumit prag minim de partiţii ale sarcinilor care înregistrează erori. |
| - | === Obiectul implicit page === | + | <note tip>Proprietatea ''yarn.resourcemanager.nm.liveness-monitor.expiry-interval-ms'' (având implicit valoarea de 600000 milisecunde = 10 minute) determină perioada de timp (trecută de la ultimul mesaj periodic) pe care o aşteaptă procesul de gestiune a resurselor înainte de a considera procesul de gestiune a nodului ca fiind eşuat.</note> |
| - | Obiectul ''page'' este o referinţă către instanţa paginii JSP curente, în fapt un sinonim pentru obiectul ''this''. | + | <note important>Întrucât nu procesul de gestiune al resurselor este cel care realizează marcarea unui nod ca făcând parte din "lista neagră", este posibil ca partiţii ale noilor sarcini să fie planificate pe noduri care au fost marcate astfel de procesele specifice ale unor aplicaţii anterioare.</note> |
| - | === Obiectul implicit exception === | + | <note tip>Implicit, un nod este inclus pe "lista neagră" dacă eşuează un număr mai mare de trei partiţii ale sarcinii rulate în contextul său, însă această valoare poate fi modificată prin intermediul proprietăţii ''mapreduce.job.maxtaskfailures.per.tracker''.</note> |
| - | Obiectul ''exception'' este un wrapper ce conţine excepţia generată de către pagina anterioară, fiind folosit pentru a construi un răspuns adecvat la eroarea în cauză. Acesta este disponibil numai în paginile JSP de eroare, indicate prin atributul ''errorPage'' al directivei ''page'' spre a fi invocate în momentul în care se produc anumite erori. | + | **d)** **Erorile produse la nivelul procesului de gestiune a resurselor** produc situaţii destul de delicate întrucât în absenţa acestuia nu pot fi lansate nici sarcini şi nici containere ale partiţiilor acestora. Totuşi, el a fost proiectat astfel încât să se poată realiza recuperarea din eroare prin utilizarea unui mecanism ce utilizează puncte de control, astfel încât starea sa este reţinută pe disc. După ce procesul de gestiune al resurselor se opreşte din cauza unei erori, o nouă instanţă va fi lansată de un administrator, aceasta putând relua execuţia prin intermediul stării încărcate de pe disc, conţinând procesele de gestiune ale nodurilor din sistem precum şi aplicaţiile care rulează. |
| - | O excepţie poate fi: | + | <note tip>Mecanismul utilizat pentru stocarea pe disc de către procesul de gestiune a resurselor poate fi configurat prin proprietatea ''yarn.resourcemanager.store.class'' a cărei valoare implicită este ''manager.recovery.MemStore'' (starea este reţinută în memorie astfel încât nu este disponibilă pentru procese din alte maşini virtuale), însă vor fi realizate şi implementări care vor folosi ZooKeeper pentru a realiza recuperarea din eroare în cazul problemelor întâmpinate la nivelul procesului de gestiune a resurselor.</note> |
| - | * verificată (o eroare de utilizator care nu poate fi prevăzută de programator, generată în momentul compilării); | + | |
| - | * generată la rulare (ignorată la momentul compilării, dar care ar fi putut fi evitată prin anumite mecanisme de prevenţie); | + | |
| - | * eroare, în privinţa căreia nu se poate face mare lucru, depăşind posibilităţile de intervenţie atât din partea utilizatorului cât şi din partea programatorului. | + | |
| - | Fiind un obiect de tip ''java.lang.Throwable'', pentru un astfel de obiect pot fi apelate metodele: | + | <note>De remarcat faptul că partiţiile sarcinii nu sunt reţinute în starea procesului de gestiune a resurselor, de vreme ce sunt gestionate de procesul specific al aplicaţiei. Ca atare, informaţiile reţinute pe disc (descriind starea procesului de gestiune a resurselor) au o dimensiune mai redusă putând fi gestionate cu uşurinţă decât în cazul procesului de monitorizare a partiţiei sarcinii din cadrul implementării MapReduce "clasice".</note> |
| - | * ''String getMessage()'' – oferă un mesaj detaliat cu privire la excepţia care s-a produs; acest mesaj detaliat cu privire la excepţia care s-a produs poate fi iniţializat în constructorul obiectului ''Throwable''. | + | |
| - | * ''Throwable getCause()'' – întoarce cauza excepţiei; | + | |
| - | * ''String toString()'' – întoarce numele clasei concatenat cu mesajul detaliat referitor la excepţia care s-a produs; | + | |
| - | * ''void printStackTrace()'' – afişează rezultatul metodei ''toString()'' împreună cu stiva de erori la fluxul de ieşire pentru erori ''System.err''; | + | |
| - | * ''StackTraceElement[] getStackTrace()'' – întoarce un vector conţinând fiecare element al stivei de erori; elementul de pe poziţia 0 reprezintă vârful stivei de erori, iar ultimul element reprezintă baza stivei de erori (metoda de la apelul căreia a pornit eroarea); | + | |
| - | * ''Throwable filllnStackTrace()'' – completează stiva de erori a obiectului ''Throwable'' cu stiva de erori curentă. | + | |
| - | Aceste metode pot fi obţinute şi prin intermediul obiectului ''pageContext'' care dispune de atributele ''exception'' (prin intermediul căruia poate fi vizualizată şi stiva de erori – atributul ''stackTrace'') şi ''errorData'' (ce oferă acces la calea relativă a paginii JSP care a generat eroarea – atributul URI şi codul de statut corespunzător excepţiei – atributul ''statusCode''). | + | ==== Politici pentru Planificarea Sarcinilor ==== |
| - | Alternativ, în cadrul unui scriplet se poate folosi un bloc ''try { ... } catch'', tratarea erorii realizându-se în cadrul aceleiaşi pagini JSP. Totuşi, o astfel de abordare trebuie evitată, recurgându-se la ea numai în situaţia în care se vrea ca recuperarea din eroare să se realizeze cât mai rapid. | + | În privinţa planificării sarcinilor, iniţial Hadoop avea o abordare prin care sarcinile erau rulate în ordinea în care erau transmise de utilizatori, fiecare dintre acestea folosind întregul cluster. Utilizarea unui cluster partajat ar fi oferit oportunitatea folosirii unor resurse de dimensiuni mari pentru mai mulţi utilizatori, însă se punea problema distribuirii echitabile a acestora. Ulterior, s-a introdus posibilitatea specificării unei priorităţi pentru o sarcină astfel încât procesul de planificare a sarcinilor o va alege pe cea cu prioritatea cea mai mare cu toate că nu este implementat dreptul de preempţiune pentru ele. Astfel, în cazul mecanismului de planificare FIFO, o sarcină având o prioritate mai mare poate fi blocată de o sarcină cu prioritate inferioară ei, având o durată de execuţie semnificativă, în situaţia în care a fost pornită anterior. |
| - | ==== Filtre ==== | + | <note tip>Specificarea priorității unei sarcini putea fi realizată prin intermediul proprietăţii ''mapred.job.priority'' sau prin metoda ''setJobPriority()'' pe obiectul ''JobClient'' (specificând una din valorile ''VERY_HIGH'', ''HIGH'', ''NORMAL'', ''LOW'' sau ''VERY_LOW'').</note> |
| - | Filtrele sunt folosite pentru a intercepta cererile de la un client înainte ca acestea să acceseze anumite resurse sau pentru a gestiona răspunsurile generate de server înainte ca acestea să fie transmise înapoi. Funcţionalitatea unui filtru trebuie specificată în metoda ''doFilter()'', care va fi suprascrisă de către orice clasă ce implementează interfaţa ''javax.servlet.Filter'': | + | Curent, MapReduce implementează mai multe mecanisme de planificare. Implicit în MapReduce "clasic" este algoritmul bazat pe cozi de procese, însă există şi procese de planificare care să ruleze într-un mediu concurent, denumite mecanismul de planificare echitabilă (//eng.// Fair Scheduler) şi mecanismul folosind planificare pe bază de capacitate (//eng.// Capacity Scheduler). |
| - | <code java> | + | Prin intermediul **mecanismului de planificare echitabilă** se doreşte să se dea fiecărui utilizator o şansă egală pentru a utiliza capacitatea clusterului de-a lungul timpului. |
| - | public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws java.io.IOException, javax.servlet.ServletException | + | |
| - | </code> | + | |
| - | Pot fi implementate filtre pentru autentificare, filtre pentru compresia datelor, filtre de criptare filtre care declanşează evenimente de acces a unor resurse, filtre de conversie a imaginilor, filtre pentru jurnalizare şi auditare, lanţuri de filtre MIME-TYPE, filtre pentru parsare şi filtre XSLT care transformă conţinutul XML. | + | <note tip>Utilizarea acestui mecanism de planificare se face prin plasarea .jar-ului corespunzător în directorul ''lib'' al Hadoop şi specificând pentru proprietatea ''mapred.jobtracker.taskScheduler'' valoarea ''org.apache.hadoop.mapred.FairScheduler''. Acesta va funcţiona fără alte configurări, însă acestea sunt disponibile inclusiv prin intermediul unei interfeţe web, pentru a utiliza toate facilităţile de care dispune.</note> |
| - | În afară de această metodă, mai trebuie implementate şi metodele ''init()'' şi ''destroy()''. | + | În situaţia în care rulează o singură sarcină, acesteia îi va fi alocat întregul cluster. Pe măsură ce sunt transmise mai multe sarcini, sunt distribuite resurse pentru partiţiile acestora, astfel încât fiecare să aibă acces, în mod echitabil, la aceleaşi mijloace ca şi ceilalţi utilizatori. Astfel, o sarcină de dimensiuni mai mici va fi terminată într-un timp optim în timp ce concomitent este rulată o sarcină a cărei execuţie durează mai mult timp, aceasta înregistrând, la rândul ei, anumite progrese. |
| - | + | ||
| - | Asocierea dintre un filtru şi un Java Servlet sau pagină JSP precum şi ordinea execuţiei acestora este determinată din fişierul de configurare ''web.xml'' în care elementul ''<filter>'' indică numele filtrului, clasa asociată precum şi parametrii folosiţi la iniţializarea acestuia, iar elementul ''<filter-mapping>'' specifică asocierile (şi ordinea) dintre un filtru şi clasele Java Servlet respectiv paginile JSP. Ordinea în care sunt executate filtrele pentru un Java Servlet respectiv pagină JSP este aceeaşi cu cea în care acestea sunt mapate în fişierul de configurare ''web.xml''. Dacă se doreşte ca un filtru să fie asociat la mai multe resurse, se poate folosi masca ''%%/*%%'' în cadrul elementului ''<url-pattern>''. | + | |
| - | ==== Încărcarea unui fișier pe server ==== | + | Sarcinile fac parte dintr-un anumit grup, în mod implicit fiecărui utilizator fiindu-i repartizat un astfel de grup. În acest fel, un utilizator care transmite mai multe sarcini decât altul nu va primi mai multe resurse în cadrul clusterului. Totodată, există posibilitatea de a crea anumite grupuri care să aibă garantat un număr minim de resurse (partiţii ale sarcinii de tip map sau de tip reduce care vor rula), stabilind ponderi pentru fiecare dintre acestea. Acest mecanism implementează dreptul de preemţiune, astfel încât în situaţia în care un grup nu şi-a primit resursele necesare pentru o perioadă de timp, procesul de planificare va termina partiţii ale sarcinilor din grupuri care şi-au depăşit capacitatea, pentru a oferi acces la resursele cuvenite grupului care a primit mai puţine resurse. De asemenea, în cadrul aceluiaşi grup, sarcinile transmise de fiecare utilizator vor partaja în mod echitabil resursele de care dispune acesta. |
| - | Încărcarea unui fişier pe server (la locaţia specificată de parametrul ''file-upload'' din fişierul de configurare ''web.xml'') se face prin intermediul unui formular care specifică ca tip de criptare (''enctype'') ''multipart/form-data'' în care controlul pentru intrare (''input'') este de tip (''type'') ''file'': | + | Abordarea din cazul **mecanismului folosind planificare pe bază de capacitate** utilizează un număr de cozi pentru fiecare cluster, fiecare având alocată o anumită capacitate. Prin această caracteristică, mecanismul folosind planificare pe bază de capacitate se deosebeşte de mecanismul de planificare echitabilă, permiţând ca pentru fiecare utilizator să se specifice o anumită pondere din totalul clusterului care va fi folosită exclusiv de acesta. Ele pot fi organizate în mod ierarhic, astfel încât între ele se stabilesc relaţii de tip părinte-copil, fiind asemănătoare grupurilor. Fiecare coadă este asociată unui utilizator şi implementează în cadrul său un mecanism de planificare de tip FIFO , folosind priorităţi. Astfel, se permite utilizatorilor să îşi simuleze propriul cluster de tip MapReduce, în care aplicaţiile pe care le transmit vor fi executate în ordinea specificată şi în funcţie de priorităţile indicate. |
| - | <code xml> | + | <note>Şi mecanismul de planificare echitabilă implementează posibilitatea ca în cadrul unui grup, sarcinile să fie planificare în funcţie de ordinea în care sunt transmise de către un utilizator, asemănându-se astfel cu mecanismul folosind planificare pe bază de capacitate.</note> |
| - | <context-param> | + | |
| - | <param-name>file-upload</param-name> | + | |
| - | <param-value> | + | |
| - | My Upload Location | + | |
| - | </param-value> | + | |
| - | </context-param> | + | |
| - | </code> | + | |
| - | <code html> | + | ===== Sistemul Distribuit de Fișiere HDFS ===== |
| - | <form action="myJSPPage.jsp" method="POST" enctype="multipart/form-data"> | + | |
| - | <input type="file" name="file" size="100" /> | + | |
| - | <br /> | + | |
| - | <input type="submit" value="Upload File" /> | + | |
| - | </form> | + | |
| - | </code> | + | |
| - | Pentru procesarea în cadrul paginii JSP a încărcării fişierului pe server, poate fi folosită [[http://commons.apache.org/proper/commons-fileupload/|biblioteca commons-fileupload]] dezvoltată de Apache ce oferă posibilitatea încărcării mai multor fişiere simultan, implementând metode pentru obţinerea diferitelor proprietăţi ale resursei respective (cale absolută, denumire, dimensiune) precum şi funcţionalităţi precum citirea din memorie şi scrierea pe disc. | + | [[:laboratoare:laborator08a|Sistemul Distribuit de Fișiere HDFS]] |
| - | ==== Invocarea altor resurse ==== | + | ===== Sistemul de Gestiune pentru Baze de Date Distribuite HBase ===== |
| - | Frecvent, invocarea altor resurse se referă la redirectare, realizată atunci când documentul s-a mutat la o altă adresă sau atunci când se doreşte echilibrarea încărcării. În acest sens se foloseşte metoda ''sendRedirect()'' apelată pe obiectul ''response()'', care primeşte ca parametru URL-ul resursei către care se doreşte a se realiza redirectarea. Alternativ, acest comportament se poate obţine prin stabilirea unor valori pentru antetele HTTP: | + | [[:laboratoare:laborator08b|Sistemul de Gestiune pentru Baze de Date Distribuite HBase]] |
| - | <code java> | + | ===== API-ul Java pentru dezvoltarea de aplicaţii distribuite folosind framework-ul Hadoop ===== |
| - | response.setIntHeader("Status", response.SC_MOVED_TEMPORARILY); | + | |
| - | response.setHeader("Location", myLocation); | + | |
| - | </code> | + | |
| - | ==== Operații cu obiecte de tip dată calendaristică === | + | Dezvoltarea unei aplicaţii MapReduce folosind Hadoop presupune configurarea unui obiect de tip ''Job'' (prin specificarea formatului pe care îl au datele de intrare şi datele de ieşire şi prin indicarea claselor care implementează sarcinile de tip Map, respectiv Reduce - opţional, se poate specifica şi o clasă de tip Combiner), prin implementarea claselor care realizează funcţionalitatea claselor de tip Map, respectiv Reduce urmată de lansarea în execuţie a aplicaţiei. |
| - | În cadrul aplicaţiilor web, se lucrează destul de des cu obiecte de tipul dată calendaristică, integrarea lor în cadrul tehnologiei Java Servlets sau JSP făcându-se prin obiecte de tip ''java.util.Date'', ce pot fi create fie prin intermediul unui constructor vid (caz în care obiectul va conţine data curentă) sau al unui constructor ce primeşte un parametru de tip ''long'', ce indică durata, exprimată în milisecunde, care s-a scurs de la 1 ianuarie 1970 (GMT). Operaţiile pe care le suportă un astfel de obiect sunt ''after(Date)'', ''before(Date)'', ''clone()'', ''compareTo(Date[|Object])'', ''equals(Object)'', ''getTime()'', ''hasCode()'', ''setTime(long)'', ''toString()''. Formatarea unui astfel de obiect se poate realiza folosind clasa ''SimpleDateFormat'' în care anumite caractere codifică atributele obiectului ''Date''. | + | Clasa ''Job'' înglobează toate informaţiile despre o sarcină, controlând totodată execuţia acesteia. |
| - | + | ||
| - | ==== Funcționalitate de tip poștă electronică ==== | + | |
| - | + | ||
| - | De asemenea, se întâlneşte frecvent necesitatea de a transmite mesaje folosind poşta electronică, o astfel de funcţionalitate putând fi integrată prin [[https://java.net/projects/javamail/pages/Home|API-ul JavaMail]] şi [[http://www.oracle.com/technetwork/java/javamail/index-135046.html|framework-ul Java Activation Framework (JAF)]]. | + | |
| - | + | ||
| - | Un obiect ''Session'' necesar construirii unui mesaj ce poate fi transmis prin poşta electronică va fi construit dintr-o serie de proprietăţi între care obligatorie este ''mail.smtp.host''. În situaţia în care serverul de poştă electronică necesită autentificare, vor fi precizate şi atributele ''mail.user'' / ''mail.password''. Obiectul reprezentând mesajul propriu-zis va avea tipul ''MimeMessage'', precizarea câmpurilor corespunzătoare (''From:'', ''To:'', ''Subject:'' şi ''Text:'') realizându-se prin metodele: | + | |
| - | * ''void setFrom(InternetAddress)'' – parametrul metodei construindu-se pornind de la adresa de poştă electronică a destinatarului; | + | |
| - | * ''void addRecipients(Message.RecipientType, Address[])'', unde | + | |
| - | * tipul poate avea valorile: | + | |
| - | * ''Message.RecipientType.TO''; | + | |
| - | * ''Message.RecipientType.CC'' (CarbonCopy); | + | |
| - | * ''Message.RecipientType.BCC'' (Blind Carbon Copy). | + | |
| - | * vectorul de adrese se creează folosind constructorul ''InternetAddress'' căruia i se transmite ca parametru adresa de poştă electronică a expeditorului. | + | |
| - | * ''void setSubject(String)'' – se stabileşte subiectul mesajului; | + | |
| - | * ''void setText(String)'' – se precizează corpul mesajului; | + | |
| - | * ''void setContent(String, String)'' – se precizează corpul mesajului, având atât conţinutul, cât şi formatul precizate ca parametri; în acest mod se pot transmite documente care au alt format în afară de ''plain-text'', de exemplu HTML / XML. În acest caz, dimensiunea pe care o poate avea corpul mesajului nu este limitată decât de serverul de poştă electronică. | + | |
| - | + | ||
| - | Transmiterea propriu-zisă a mesajului se face prin metoda statică ''send()'' definită în clasa ''Transport'', aceasta generând o excepţie ''MessagingException'' dacă mesajul nu a putut fi transmis din diverse motive. | + | |
| - | + | ||
| - | De asemenea, pot fi incluse ataşamente prin construirea unui obiect MimeMultiPart la care se adaugă (folosind metoda ''addBodyPart()'') fragmente ale ataşamentului respectiv, de tipul ''MimeBodyPart''. Un astfel de obiect suportă metodele ''setText(String)'' pentru a indica o corpul mesajului, respectiv ''setDataHandler(DataHandler)'' şi ''setFileName(String)'' pentru a specifica fişierul care conţine ataşamentul. Indicarea conţinutului ca fiind un obiect de tipul MimeMultiPart se face tot prin metoda ''setContent()''. | + | |
| - | + | ||
| - | Un obiect de tip ''DataHandler'' se obţine dintr-un obiect ''FileDataSource'', care la rândul său este construit pornind de la denumirea fişierului în cauză, la care se adaugă şi calea către el, relativă sau absolută. În situaţia în care nu se specifică nici o cale, fişierul va fi căutat în acelaşi director în care este plasată clasa Java Servlet sau pagina JSP care încearcă transmiterea sa prin intermediul poştei electronice. | + | |
| - | + | ||
| - | Informaţiile cu privire la destinatar, expeditor, subiect şi conţinut pot fi incluse într-un formular, apoi preluate din obiectul ''request'' astfel încât transmiterea mesajului să se facă pe baza acestora. | + | |
| <code java> | <code java> | ||
| - | <%@ page import="javax.mail.*", "javax.mail.internet.*", "javax.activation.*" %> | + | Job job = Job.getInstance(getConf(), "JobName"); |
| - | ... | + | |
| - | <% | + | |
| - | Properties properties = System.getProperties(); | + | |
| - | properties.setProperty("mail.smtp.host","localhost"); | + | |
| - | properties.setProperty("mail.user",myUser); | + | |
| - | properties.setProperty("mail.password",myPassword); | + | |
| - | Session mailSession = Session.getDefaultInstance(properties); | + | |
| - | try { | + | |
| - | MimeMessage mailMimeMessage = new MimeMessage(maiSession); | + | |
| - | mailMimeMessage.setFrom(new InternetAddress(from)); | + | |
| - | mailMimeMessage.addRecipient(Message.RecipientType.TO, new InternetAddress(to)); | + | |
| - | mailMimeMessage.setSubject("My Subject"); | + | |
| - | Multipart mailMultipart = new MimeMultipart(); | + | |
| - | BodyPart mailBodyPart; | + | |
| - | mailBodyPart = new MimeBodyPart(); | + | |
| - | mailBodyPart.setText("My Mail Body"); | + | |
| - | mailMultipart.addBodyPart(mailBodyPart); | + | |
| - | mailBodyPart = new MimeBodyPart(); | + | |
| - | String filename = "myFileName.myExtension"; | + | |
| - | mailBodyPart.setDataHandler(new DataHandler(new FileDataSource(filename))); | + | |
| - | mailBodyPart.setFileName(filename); | + | |
| - | mailMultiPart.addBodyPart(mailBodyPart); | + | |
| - | mailMimeMessage.setContent(mailMultiPart); | + | |
| - | Transport.send(mailMimeMessage); | + | |
| - | } catch (MessagingException exception) { | + | |
| - | System.out.println("An exception has occurred: "+exception.getMessage(); | + | |
| - | if (Constants.DEBUG) | + | |
| - | exception.printStackTrace(); | + | |
| - | } | + | |
| - | %> | + | |
| </code> | </code> | ||
| - | ===== Utilizarea EL (Expression Language) în pagini JSP ===== | + | O sarcină este împachetată în cadrul unei arhive ''.jar'' care este distribuită în mod automat de sistemul Hadoop. Pentru a şti care este sarcina de transmis, trebuie apelată metoda ''setJarByClass()'', primind ca argument chiar arhiva ''.jar'' în care se găseşte sarcina curentă. Sistemul Hadoop va localiza prin aceasta fişierul care conţine clasa indicată. |
| - | + | ||
| - | EL (Expression Language) pune la dispoziţia utilizatorilor un mecanism prin care nivelul de prezentare poate comunica cu nivelul de logică al aplicaţiei (mai ales componente Java Beans), implementând operaţii aritmetice şi logice folosind tipuri de date întregi, reale, şiruri de caractere, valori adevărat / fals şi ''null''. | + | |
| - | + | ||
| - | Tehnologia Java Server Pages foloseşte Expression Language pentru: | + | |
| - | * evaluarea imediată şi leneşă a expresiilor prin realizarea de operaţii aritmetice şi logice în mod dinamic; | + | |
| - | * posibilitatea de a stabili şi de a obţine informaţii (având structuri de date variate), în special proprietăţi ale componentelor Java Beans, pe baza interacţiunii cu utilizatorul; | + | |
| - | * folosirea de obiecte implicite; | + | |
| - | * abilitatea de a invoca metode (publice şi statice). | + | |
| - | + | ||
| - | În Expression Language pot fi folosite următoarele tipuri de expresii: | + | |
| - | * **expresii cu evaluare imediată** (a căror valoare este stabilită instant de către tehnologia împreună cu care este utilizată, în cazul de faţă Java Server Pages) şi **expresii cu evaluare leneşă** (a căror valoare poate fi evaluată ulterior de către tehnologia împreună cu care este utilizată); | + | |
| - | * **expresii valoare** (ce referă date) şi **expresii metodă** (ce invocă metode); | + | |
| - | * **expresii rvalue** (care pot doar să citească date dintr-un obiect extern) şi **expresii lvalue** (care pot atât să citească cât şi să scrie date din / într-un obiect extern). | + | |
| - | + | ||
| - | Expresiile cu evaluare imediată, desemnate prin sintaxa ''${expression}'', sunt calculate înainte ca pagina Internet să fie afişată. Aceste expresii pot fi folosite numai ca valori ale unei etichete care acceptă expresii evaluate în momentul rulării şi sunt întotdeauna expresii de tip rvalue. | + | |
| <code java> | <code java> | ||
| - | <input type="submit" name="${inputName}" value="${inputValue}"> | + | job.setJarByClass(getClass()); |
| </code> | </code> | ||
| - | Pentru expresiile cu evaluare leneşă, pentru care se foloseşte sintaxa ''#{expression}'', valoarea poate fi determinată mai târziu, prin mecanisme ce ţin de tehnologia împreună cu care e folosită Expression Language, pe parcursul ciclului de viaţă al paginii Internet. Aceste expresii pot fi expresii valoare utilizate atât pentru a citi cât şi pentru a scrie informaţii dar şi expresii metodă. | + | În continuare, trebuie specificat formatul datelor de intrare respectiv al datelor de ieşire, acestea putând fi fişiere, directoare, texte sau tabele dintr-o bază de date. |
| + | |||
| + | Datele de intrare sunt indicate de o implementare a clasei ''InputFormat'', responsabilă de crearea partiţiilor şi a sistemului de preluare a înregistrărilor. | ||
| <code java> | <code java> | ||
| - | <table><tr><td>#{inputName}</td><td>#{inputValue}</td></tr></table> | + | job.setInputFormatClass(CustomInputFormat.class); |
| </code> | </code> | ||
| - | <note>De regulă, expresiile cu evaluare leneşă sunt folosite mai des împreună cu JavaServer Faces pentru că această tehnologie are un ciclu de viaţă împărţit în mai multe faze, astfel încât determinarea valorii unei expresii nu se poate realiza decât la un anumit moment, după tratarea evenimentelor cu privire la componente şi validarea datelor.</note> | + | <note tip>În cazul în care datele de intrare nu respectă formatul nici uneia dintre clasele care implementează interfaţa ''InputFormat'', va fi specificată o clasă definită de utilizator care defineşte metoda ''createRecordReader(InputSplit, TaskAttemptContext)'' întorcând obiectul (derivat din clasa ''RecordReader'') care gestionează preluarea înregistrărilor din sursa respectivă.</note> |
| - | Expresiile de tip valoare pot fi clasificate în expresii rvalue şi lvalue după cum dispun sau nu de posibilitatea de a scrie informaţii. Expresiile cu evaluare imediată sunt întotdeauna expresii rvalue, în timp ce expresiile cu evaluare leneşă sunt de regulă expresii lvalue. | + | După ce sunt preluate înregistrările din sursa de date, acestea sunt partiţionate şi fiecare secvenţă este transmisă unui proces de tip Mapper spre a fi prelucrată în continuare. |
| - | Expresia ''${entitybean.attribute}'' obţine valoarea unui atribut din cadrul unei componente Java Bean, evaluându-l instant şi transmiţând mai departe rezultatul obţinut. | + | Hadoop foloseşte propriul mecanism de serializare pentru transmiterea datelor prin infrastructura de comunicaţie, fiind optimizat pentru cazul reţelelor de calculatoare. Serializarea este folosită pentru interacțiunea cu fișiere și cu baze de date. În pachetul ''org.apache.hadoop.io'' sunt definite clasele ''IntWritable'' (pentru ''Integer''), ''Text'' (pentru ''String'') şi altele, însă implementarea propriilor clase pentru tipuri de date serializabile este destul de facilă. |
| - | Expresia ''#{entitybean.attribute}'' poate avea acelaşi comportament ca expresia de mai sus, însă eticheta în cadrul căreia este folosită poate decide evaluarea sa la un moment de timp ulterior. Mai mult, atunci când pagina Internet este solicitată, expresia va fi de tipul rvalue (obţinând valoarea atributului), în timp ce în cadrul etapei postback ea poate fi de tipul lvalue (putând stabili valoarea atributului). | + | Datele de ieşire sunt specificate de o implementare a clasei ''OutputFormat'', indicându-se şi locaţia la care sarcinile de tip reduce îşi vor plasa rezultatele. Fiecare sarcină de tip reduce dispune de propriile date de ieşire, de tip text, sub forma unor perechi (cheie, valoare). |
| - | + | ||
| - | Toate expresiile pot referi următoarele tipuri de obiecte, împreună cu atributele lor: componente Java Beans, colecţii, structuri de date Java de tip enumerare, obiecte implicite. Pentru a le utiliza, se foloseşte o expresie în care variabila este reprezentată de denumirea obiectului: | + | |
| <code java> | <code java> | ||
| - | ${myObject} | + | job.setOutputFormatClass(CustomOutputFormat.class); |
| - | #{myObject} | + | |
| </code> | </code> | ||
| - | <note tip>În cazul structurilor de date Java de tip enumerare, elementele pot fi referite folosind tipul ''String''. Pentru tipul de dată ''public enum EnumType {attribute<sub>1</sub>, atrribute<sub>2</sub>, ..., attribute<sub>n</sub>}'', dacă este întâlnit un şir de caractere având valoarea ''${"atrribute<sub>k</sub>"}'', acesta va fi convertit automat la tipul ''EnumType.atrribute<sub>k</sub>''.</note> | + | <note important>Locaţia la care vor fi plasate datele de ieşire trebuie să nu existe, în caz contrar generându-se o excepţie care determină eşuarea globală a sarcinii.</note> |
| - | Container-ul paginii Internet evaluează variabila ce apare în expresie căutându-i valoarea ce este oferită de ''PageContext.findAttribute("myObject")'', domeniile de vizibilitate investigate fiind ''page'', ''request'', ''session'' şi ''application''. Dacă obiectul nu este găsit, se returnează o valoare ''null''. Pot fi utilizare motoare EL particularizate pentru a se modifica modul în care sunt evaluate expresiile. | + | <note tip>În mod implicit, este definit un singur proces de tip reduce.</note> |
| - | Pentru a referi proprietăţile unei componente Java Bean sau a instanţei unei enumerări, elementele unei colecţii sau atributele unui obiect implicit pot fi folosite notaţiile ''.'' sau ''[]'', care sunt echivalente. Pentru şirurile de caractere se pot folosi atât apostroafe, cât şi ghilimele. | + | Pentru fiecare sarcină trebuie specificat formatul pentru cheia şi valoarea datelor de ieşire, pentru procesele de tip map cât şi pentru procesele de tip reduce. Sunt folosite în acest scop metodele ''setMapOutputKeyClass()'' şi ''setMapOutputValueClass()'', care pot primi ca parametrii tipuri predefinite de date sau tipuri de date definite de utilizator. |
| - | Următoarele expresii sunt echivalente: | + | În cazul în care datele de intrare sau datele de ieşire au tipurile ''TextInputFormat'' respectiv ''TextOutputFormat'', locaţia de la care pot fi preluate informaţiile va fi indicată prin metodele ''FileInputFormat.setInputPaths()'', respectiv ''FileOutputFormat.setOutputPath()'' care vor primi ca parametru obiectul de tip ''Job'' şi un obiect de tip ''Path'' care conţine calea din sistemul de fişiere local sau din cadrul sistemului distribuit de fişiere HDFS. |
| - | <code java> | + | Ulterior, trebuie indicate clasele de tip Mapper şi Reducer (având de regulă aceeaşi cheie pentru datele de ieşire): |
| - | ${myObject.myAttribute} | + | |
| - | </code> | + | |
| <code java> | <code java> | ||
| - | ${myObject['myAttribute']} | + | job.setMapperClass(MyMapper.class); |
| + | job.setReducerClass(MyReducer.class); | ||
| </code> | </code> | ||
| - | <code java> | + | În continuare, sarcina este lansată în execuţie prin apelarea metodei ''waitForCompletion()'' ce primeşte un parametru care indică dacă sunt afişate valori la consolă. Metoda transmite sarcina, aşteptând terminarea acesteia. Rezultatul pe care îl întoarce este ''true'' în caz că sarcina a fost terminată cu succes şi ''false'' în caz contrar. |
| - | ${myObject["myAttribute"]} | + | |
| - | </code> | + | |
| - | Expresii similare pot fi formate şi pentru asigurarea evaluării leneşe. | + | **Clasa de tip ''Map''** este derivată din clasa ''org.apache.hadoop.mapreduce.Mapper'', fiind parametrizată cu 4 valori de tip JavaGenerics având semnificaţia tipurilor de date pe care le au cheia şi valoarea datelor de intrare, respectiv cheia şi valoarea datelor de ieşire. Sarcina programatorului constă în a defini metoda ''map()'' care primeşte ca parametru o cheie şi o valoare (cu tipurile aferente celor specificate la parametrizarea clasei) corespunzătoare datelor de intrare şi un obiect de tip ''Context'' folosit pentru datele de ieşire, dar şi pentru specificarea unor contoare definite de utilizator. |
| - | De asemenea, pot fi folosite şi combinaţii ale operatorilor ''.'' şi ''[]'' în cadrul aceleiaşi expresii. | + | <file java MyMapper.java> |
| + | public class MyMapper extends Mapper<LongWritable, Text, Text, Text> { | ||
| - | <note important>În cazul componentelor JavaBeans, un atribut ''myAttribute'' poate fi referit în cadrul unei expresii EL doar în situaţia în care este implementată o metodă getter de acces la acesta, având numele ''getMyAttribute()''.</note> | + | @Override |
| + | public void map(LongWritable key, Text value, Context context) | ||
| + | throws IOException, InterruptedException { | ||
| + | Text outputKey = new Text(); | ||
| + | Text outputValue = new Text(); | ||
| + | // ... | ||
| + | context.write(outputKey, outputValue); | ||
| + | } | ||
| + | } | ||
| + | </file> | ||
| - | Pentru accesarea elementelor unui vector sau al unei liste, trebuie folosite valori literale (fără apostroafe sau ghilimele) ce pot fi convertite la un întreg sau notaţia ''[]'' ce primeşte de asemenea un întreg, în timp ce pentru un obiect de tip ''Map'' accesarea implică folosirea unei chei de tip şir de caractere: | + | Analog, **clasa de tip ''Reduce''** este derivată din ''org.apache.hadoop.mapreduce.Reducer'', având acelaşi tip de parametrizare cu menţiunea faptului că tipurile specificate pentru datele de intrare (pentru cheie şi pentru valoare) ale acestuia trebuie să fie identice cu tipurile specificate pentru datele de ieşire ale clasei de tip ''Map'', întrucât perechile (cheie, valoare) produse de aceasta vor fi grupate astfel că pentru fiecare cheie va exista un set de una sau mai multe valori. Acest proces este cunoscut sub denumirea de amestecare şi sortare (//eng.// shuffle and sort). Intrările pentru clasa de tip ''Reduce'' sunt sortate în funcţie de cheie, fiecare set de valori fiind prelucrat în metoda ''reduce()'' care primeşte ca argumente o cheie şi un set de valori, dar şi obiectul de tip ''Context''. Un set de valori este reprezentat sub forma ''Iterable<V>''. Prelucrările în cadrul metodei ''reduce()'' sunt realizate prin parcurgerea acestei liste şi realizarea unor agregări asupra acestor valori pentru obţinerea rezultatului. |
| - | <code java> | + | <file java MyReducer.java> |
| - | // array or list: firstComponent can be narrowed to 0 | + | public class MyReducer extends Reducer<Text, Text, Text, Text> { |
| - | ${myObject.myAttribute[0]} | + | |
| - | ${myObject.myAttribute[firstComponent]} | + | |
| - | </code> | + | |
| - | <code java> | + | @Override |
| - | // map | + | public void reduce(Text key, Interable<Text> values, Context context) |
| - | ${myObject.myAttribute["firstComponent"]} | + | throws IOException, InterruptedException { |
| - | </code> | + | Text outputKey = new Text(); |
| + | Text outputValue = new Text(); | ||
| + | for (Text value: values) { | ||
| + | // ... | ||
| + | } | ||
| + | context.write(outputKey, outputValue); | ||
| + | } | ||
| + | } | ||
| + | </file> | ||
| - | În Expression Language pot fi folosite următoarele tipuri de date: | + | Opţional, se poate defini şi o clasă Combiner, spre a combina informaţiile pentru fiecare sarcină de tip ''Map'', astfel încât cantitatea datelor procesate de sarcina ''Reduce'' să fie limitată. Aceste clase nu sunt însă garantate să ruleze, fiind folosite mai mult pentru optimizări şi nu în cazul operaţiilor critice pentru logica aplicaţiei. |
| - | * ''Boolean'': poate avea valorile ''true'' sau ''false''; | + | |
| - | * numere întregi, numere reale: utilizate la fel ca în Java; | + | |
| - | * ''String'' (şir de caractere), referite cu apostroafe sau ghilimele, ''\'' fiind folosit pe post de caracter escape (''"'' → ''\"'', ''''' → ''\''', ''\'' → ''\\''); | + | |
| - | * ''null''. | + | |
| - | Expresiile de tip valoare pot fi utilizate în text static sau în etichete (standard sau definite de utilizator) care acceptă o expresie. Unele etichete acceptă doar expresii de tip rvalue în timp ce alte expresii pot accepta şi expresii de tip lvalue. | + | Informaţiile oferite la rularea unei sarcini sunt identificatorul acesteia (folosit pentru a monitoriza şi a gestiona sarcina), numărul de partiţii generate, progresul şi valoarea contoarelor exprimând statistici referitoare la operaţiile care au fost realizate în cadrul rulării sarcinii. |
| - | Valoarea unei expresii în text static este evaluată şi ulterior inclusă în pagina Internet: | + | Întrucât HBase este scris în limbajul de programare Java, el oferă un API pentru acces programatic la informaţiile conţinute. Aceasta reprezintă metoda cea mai rapidă de a accesa informaţiile din cadrul unei baze de date distribuite de tip HBase. Sunt suportate operaţii de tip CRUD, dar şi unele operaţii administrative. |
| - | + | ||
| - | <code java> | + | |
| - | <my:tag>my text1 ${expression} my text2</my:tag> | + | |
| - | </code> | + | |
| - | + | ||
| - | Atributul valoare al unei etichete poate fi iniţializat folosind o expresie rvalue sau lvalue astfel: | + | |
| - | * folosind o singură expresie; expresiile sunt evaluate şi rezultatul convertit la tipul atributului respectiv: <code java> | + | |
| - | <my:tag value="${expression}" /> | + | |
| - | <my:tag value="#{expression}" /> | + | |
| - | </code> | + | |
| - | * folosind una sau mai multe expresii, separate sau înconjurate de text; se numesc **expresii compuse** şi sunt evaluate de la stânga la dreapta, fiecare expresie fiind convertită la tipul ''String'' şi concatenată cu alte texte, şirul de caractere rezultat fiind convertit la tipul atributului respectiv: <code java> | + | |
| - | <my:tag value="my text1 ${expression1} my text2 ${expression2}" /> | + | |
| - | <my:tag value="my text1 #{expression1} my text2 #{expression2}" /> | + | |
| - | </code> În EL 3.0 operatorul ''+'' e folosit pentru concatenarea şirurilor de caractere. | + | |
| - | * folosind doar text; se numesc **expresii literale** şi valoarea ''String'' a atributului este converită la tipul atributului respectiv <code java> | + | |
| - | <my:tag value="my text" /> | + | |
| - | </code> | + | |
| - | Toate expresiile utilizate pentru a stabili valori sunt evaluate în contextul unui tip de date aşteptat. Dacă rezultatul obţinut în urma evaluării expresiei nu corespunde exact tipului de date aşteptat, se va realiza o conversie de tip. | + | Accesul la o bază de date HBase presupune crearea unui obiect de tip ''Configuration'', în care sunt încărcate proprietăţi specifice, indicate de utilizator în fişierele de configurare, construirea unui obiect de tip ''HTable'' (pentru care trebuie să se specifice atât obiectul de configurare cât şi denumirea tabelului), realizarea diferitelor operaţii (''put()'', ''get()'', ''scan()'', ''delete()'') şi închiderea instanţei care implică eliberarea tuturor resurselor folosite şi a zonelor de memorie tampon. |
| - | Expresiile de tip metodă permit invocarea unei metode implementate în cadrul unei componente Java Bean, care întoarce un rezultat. De regulă, acestea sunt folosite pentru a realiza anumite procesări asociate cu controlul din contextul căruia sunt apelate, fiind invocate în momentul în care este generat un eveniment în cadrul acelui control. Întrucât o metodă poate fi invocată pe parcursul diferitelor etape ale ciclului de viaţă, expresiile de tip metodă trebuie să fie întotdeauna cu evaluare leneşă. | + | Clasa ''Configuration'' extinde clasa de configurare Hadoop cu care păstrează compatibilitatea, la crearea unui astfel de obiect fiind încărcate din classpath documentele ''hbase-default.xml'' şi ''hbase-site.xml''. Valorile proprietăţilor din ''hbase-site.xml'' suprascriu valorile proprietăţilor conţinute în ''hbase-default.xml''. |
| <code java> | <code java> | ||
| - | <my:tag id="myTagId" action="#{myObject.myValue}" /> | + | Configuration conf = HBaseConfiguration.create(); |
| - | <my:tag id="myTagId" action="#{myObject['myValue']}" /> | + | |
| </code> | </code> | ||
| - | Expresiile de tip metodă trebuie folosite întotdeauna numai în cadrul unei etichete, fie folosind o singură expresie care este evaluată şi transmisă modulului de gestiune a etichetei, putând fi invocată mai târziu sau folosind doar text. La execuţia metodei asociată expresiei în cauză, se întoarce un rezultat de tip ''String'' care este convertit la tipul aşteptat, aşa cum este definit de descriptorul bibliotecii de etichete. | + | Unele proprietăţi pot fi suprascrise manual, însă un astfel de comportament nu este de regulă necesar şi nici de dorit. |
| - | + | ||
| - | Expresiile de tip metodă pot fi utilizate pentru a invoca funcţii care primesc un set de parametrii (0 sau mai mulţi, separaţi prin virgulă). Se pot folosi atât operatorul ''.'' cât şi ''[]'', comportamentul acestora fiind similar: | + | |
| <code java> | <code java> | ||
| - | myObject["myMethod"](myparameters) | + | conf.set("mapreduce.input.keyvaluelinerecordreader.key.value.separator", "\t"); |
| - | myObject.myMethod(myParameters) | + | |
| </code> | </code> | ||
| - | În Expression Language pot fi folosite şi expresii lambda, acestea fiind expresii de tip valoare ce primesc parametrii. Utilizarea acestora este comparabilă cu cazul limbajului de programare Java, cu excepţia faptului că şi corpul expresiei lambda este tot o expresie EL. O expresie lambda foloseşte operatorul ''->'', identificatorii din stânga expresiei fiind parametrii lambda, în dreapta expresiei aflându-se o expresie EL. De obicei, se folosesc parantezele ''()'' pentru a include parametrii lambda, acestea putând fi omise dacă există un singur parametru. | + | Un obiect de tip ''Configuration'' trebuie partajat cât de mult posibil întrucât obiectele de tip ''HTable'' create cu aceeaşi configurație vor avea aceeaşi conexiune la ZooKeeper şi HBaseMaster, de vreme ce o conexiune este reţinută intern printr-un obiect de tip asociere care foloseşte drept cheie instanţe ale obiectului de tip ''Configuration''. Când se reutilizează un obiect de tip ''Configuration'' pentru mai multe instanţe ''HTable'', trebuie apelat ''close()'' ca ''HConnectionManager'' să elimine instanţa din lista tabelelor ce folosesc conexiunea respectivă. Când toate tabelele unei conexiuni sunt închise, este terminată şi conexiunea corespunzătoare. O conexiune e reprezentată de clasa ''HConnection'' fiind gestionată de clasa ''HConnectionManager''. |
| - | O expresie lambda are un comportament asemănător cu al unei funcţii, aceasta putând fi apelată imediat sau valoarea ei poate fi asociată unei variabile ce va fi utilizată ulterior pentru invocarea metodei respective primind parametrii care vor fi disponibili la momentul respectiv. | + | Clasa ''org.apache.hadoop.client.HTable'' reprezintă interfaţa clientului către o singură tabelă HBase, oferind acces la operaţii CRUD, fiind proiectată pentru a fi utilizată cu uşurinţă. Operaţiile care modifică datele sunt atomice pentru fiecare înregistrare în parte, neexistând conceptul de tranzacţie (care să implice mai mult de o înregistrare). Astfel, există o consistenţă de 100% per înregistrare, clientul realizând operaţii de citire sau de scriere asupra acesteia sau aşteptând eliberarea accesului. Atomicitatea se păstrează indiferent de numărul de coloane utilizate în cadrul cererii. |
| - | O expresie lambda poate fi transmisă ca argument al unei metode, fiind executată în cadrul metodei respective sau poate fi inclusă la rândul ei, în altă expresie lamda. | + | <note>Un obiect ''HTable'' poate fi utilizat pentru grupuri de comenzi (spre a se asigura performanţa), însă aceste operaţii nu sunt atomice.</note> |
| - | <code java> | + | <note>O astfel de abordare nu reprezintă o problemă în cazul în care există mai multe procese care realizează operaţii de citire, însă există o problemă în cazul în care mai multe procese încearcă să scrie aceeaşi înregistrare.</note> |
| - | () -> null | + | |
| - | (x,y) -> (x+y)/2 | + | |
| - | ((x,y) -> Math.sqrt(x*y))(4,9) | + | |
| - | distance = (x1,y1,x2,y2) -> Math.sqrt(Math.sqr(x1-x2)+Math.sqr(y1-y2)); | + | |
| - | distance(1,1,2,2) | + | |
| - | </code> | + | |
| - | O expresie literală este evaluată la textul expresiei, care are tipul ''String'', fiind utilizată atunci când este necesară folosirea sintaxei rezervate ''${}'' sau ''#{}''. Dacă se doreşte folosirea şirului de caractere ''${expression}'' se pot folosi alternativele ''${'${'}expression'' sau ''\${expression}'' (idem şi pentru expresii ''#{}''). O expresie literală nu foloseşte delimitatorii ''${}'' sau ''#{}''. | + | Crearea unui obiect ''HTable'' este destul de costisitoare întrucât este scanat întregul catalog ''.META.'', verificându-se dacă tabela există şi este activată. Astfel, aceasta trebuie apelată o singură dată în cadrul unui fir de execuţie, fiind refolosit cât de mult posibil. Operaţiile realizate cu obiecte ''HTable'' nu sunt garantate să funcţioneze corect într-un mediu distribuit. |
| - | + | ||
| - | Expresiile literale pot fi cu evaluare imediată sau cu evaluare leneşă şi pot fi atât expresii de tip valoare cât şi expresii de tip metodă. Momentul la care este evaluată o expresie depinde de contextul în care aceasta este utilizată. | + | |
| - | În EL sunt suportate operaţii pe colecţii cum ar fi: | + | <code java> |
| - | * mulţimi <code> | + | HTable myTable = new HTable(conf, "TableName"); |
| - | {1, 2, ..., n} | + | |
| - | </code> | + | |
| - | * liste – acestea pot conţine elemente având tipuri diferite <code> | + | |
| - | {1, two, "3", ...} | + | |
| - | </code> | + | |
| - | * obiecte de tip map <code> | + | |
| - | {"one":1, "two":2, ..., "n":n} | + | |
| </code> | </code> | ||
| - | Prin Expression Language, se permite crearea dinamică a acestora, exploatarea lor implicând utilizarea unor obiecte de tip flux, respectiv benzi de asamblare (//eng.// pipeline). Astfel, metodele vor fi apelate pe fluxul de elemente obţinut din colecţia respectivă folosind metoda ''stream()''. Întrucât unele metode întorc tot un flux de elemente, acestea pot fi înlănţuite împreună în cadrul unei benzi de asamblare . Operaţiile realizate pe fluxul de elemente nu modifică colecţia originală. | + | <note tip>În cazul în care se folosesc prea multe instanţe ''HTable'' la un moment dat, ar trebui utilizat un obiect de tip ''HTablePool'' pentru că reutilizează instanţele acestor clase.</note> |
| - | <note>Metoda ''stream()'' obţine un obiect ''Stream'' pornind de la un obiect ''java.util.Collection'' sau un vector Java.</note> | + | Pentru a se scrie date într-o tabelă, se foloseşte un obiect de tip ''Put'', care este instanţiat cu identificatorul (cheia de rând) a înregistrării care se doreşte transmisă, în format binar. |
| - | + | ||
| - | <note>O bandă de asamblare constă dintr-o sursă (obiect de tip ''Stream''), orice număr de operaţii intermediare care întorc un flux (spre exemplu, ''filter()'' şi ''map()'') şi o operaţie terminală care nu întoarce un flux (spre exemplu, ''toList()'').</note> | + | |
| <code java> | <code java> | ||
| - | collection.stream().filter(a->a.attribute1=='value1') | + | Put myPut = new Put(Bytes.toBytes("myRow")); |
| - | .map(a->a.attribute2) | + | |
| - | .sorted() | + | |
| - | .toList() | + | |
| </code> | </code> | ||
| - | Operaţiile suportate de fluxul de elemente sunt: ''allMatch()'', ''anyMatch()'', ''average()'', ''count()'', ''distinct()'', ''filter()'', ''findFirst()'', ''flatMap()'', ''forEach()'', ''iterator()'', ''limit()'', ''map()'', ''max()'', ''min()'', ''noneMatch()'', ''peek()'', ''reduce()'', ''sorted()'', ''substream()'', ''sum()'', ''toArray()'', ''toList()''. | + | Un astfel de obiect implementează metoda ''add()'', prin care se indică coordonatele celulei la care se doreşte să se scrie valoarea în cauză. |
| - | + | ||
| - | În afară de operatorii ''.'' şi ''[]'', Expression Language pune la dispoziţia programatorilor şi alţi operatori, ce pot fi folosiţi doar în expresii de tip rvalue: | + | |
| - | + | ||
| - | ^ Categorie Operator ^ Operator ^ Precedenţă ^ Observaţii ^ | + | |
| - | | | ''.'' ''[]'' | 1 | | | + | |
| - | | | ''()'' | | folosit pentru a schimba precedenţa operatorilor | | + | |
| - | | aritmetici | ''-'' (unar) | 2 | | | + | |
| - | | logici | ''not'' ''!'' | 2 | | | + | |
| - | | empty | ''empty'' | 2 | utilizat pentru a verifica dacă o valoare este ''null'' sau vidă | | + | |
| - | | aritmetici | ''*'' ''/'' ''div'' ''%'' ''mod'' | 3 | | | + | |
| - | | ::: | ''+'' ''-'' (binar) | 4 | | | + | |
| - | | concatenare şiruri de caractere | ''+='' | 5 | | | + | |
| - | | relaţionali | ''%%<>%%'' ''%%<%%'' ''%%<=%%'' ''%%>%%'' ''%%>=%%'' ''lt'' ''le'' ''gt'' ''ge'' | 6 | comparaţia se poate face cu alte valori sau literali | | + | |
| - | | ::: | ''=='' ''!='' ''eq'' ''ne'' | 7 | | | + | |
| - | | logici | ''&&'' ''and'' | 8 | | | + | |
| - | | ::: | ''||'' ''or'' | 9 | | | + | |
| - | | condiţionali | ''?'' '':'' | 10 | ''X ? Y : Z'' | | + | |
| - | | lambda | ''->'' | 11 | | | + | |
| - | | atribuire | ''='' | 12 | | | + | |
| - | | | '';'' | 13 | | | + | |
| - | + | ||
| - | În Expression Language există şi termeni rezervaţi, care nu pot fi utilizaţi ca identificatori: ''and'', ''or'', ''not'', ''eq'', ''ne'', ''lt'', ''gt'', ''le'', ''ge'', ''true'', ''false'', ''null'', ''instanceof'', ''empty'', ''div'', ''mod''. | + | |
| - | + | ||
| - | Există posibilitatea ca expresiile EL să nu fie evaluate în cazul în care atributul ''isELIgnored'' al directivei ''page'' are valoarea ''true'' (implicit, valoarea sa este ''false'', deci în mod obişnuit, expresiile EL vor fi evaluate într-o pagină JSP). | + | |
| - | + | ||
| - | ===== Utilizarea JSTL (JavaServer Pages Standard Tag Library) pentru implementarea unor funcţionalităţi complexe ===== | + | |
| - | + | ||
| - | JSTL (JavaServer Pages Standard Tag Library) este o colecţie de etichete JSP, care implementează funcţionalitatea de bază specifică pentru numeroase aplicaţii JSP. | + | |
| - | + | ||
| - | Etichetele JSTL pot fi clasificate, în funcţie de comportamentul pe care îl oferă utilizatorilor în mai multe grupuri: | + | |
| - | - etichete JSTL de bază; | + | |
| - | - etichete JSTL pentru formatare; | + | |
| - | - etichete JSTL destinate gestiunii informaţiilor dintr-o bază de date SQL; | + | |
| - | - etichete JSTL pentru gestiunea documentelor XML; | + | |
| - | - funcţii JSTL. | + | |
| - | + | ||
| - | Pentru ca aceste funcţionalităţi să fie accesibile în pagina JSP a unei aplicaţii web, biblioteca JSTL trebuie plasată în directorul ''WEB-INF\lib'' corespunzător acesteia. | + | |
| - | + | ||
| - | ==== Etichete JSTL de bază ==== | + | |
| - | + | ||
| - | **Etichetele JSTL de bază** sunt cele mai frecvent utilizate, implementând funcţionalitate legată de controlul fluxului. | + | |
| - | + | ||
| - | Pentru a putea referi aceste etichete, trebuie specificată locaţia de unde acestea pot fi încărcate. | + | |
| <code java> | <code java> | ||
| - | <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> | + | Put.add(family, column, value); |
| + | Put.add(family, column, value, timestamp, value); | ||
| </code> | </code> | ||
| - | Cele mai frecvent utilizate etichete JSTL de bază sunt: | + | Toţi parametrii sunt în format binar, fiind reponsabilitatea programatorului conversia acestora. |
| - | | ''<c:out>'' | evaluează valoarea unei expresii pe care o afişează\\ Atributele asociate etichetei sunt:\\ • ''value'' (obligatoriu) – informaţia care trebuie evaluată;\\ • ''default'' – informaţie asociată în cazul în care evaluarea eşuează;\\ • ''escapeXml'' – utilizată pentru a ignora caractere XML speciale. <code java> | + | Transferul propriu-zis al datelor se realizează la apelul metodei ''put()'', pe instanţa ''HTable'': |
| - | <c:out value="${myObject.myAttribute}" /> | + | |
| - | </code> | | + | |
| - | | ''<c:set>'' | asociază rezultatul evaluării unei expresii la o variabilă, disponibilă pentru un anumit domeniu de vizibilitate.\\ \\ Atributele asociate etichetei sunt:\\ • ''value'' – informaţia care trebuie evaluată şi asociată obiectului;\\ • ''target'' – denumirea obiectului a cărui proprietate trebuie stabilită;\\ • ''property'' – proprietate a obiectului ''target'' a cărui valoare va fi stabilită la valoarea indicată (trebuie specificat dacă se foloseşte ''target'');\\ • ''var'' – denumirea variabilei care va stoca informaţia;\\ • ''scope'' – scopul variabilei care va stoca informaţia. <code java> | + | |
| - | <c:set value="${expression}" target="myObject" property="myAttribute" scope="page" /> | + | |
| - | <c:set value="${expression}" var="myVariabile" scope="application" /> | + | |
| - | </code> | | + | |
| - | | ''<c:remove>'' | disociază o variabilă de contextul unui anumit domeniu de vizibilitate\\ \\ Atributele asociate etichetei sunt:\\ • ''var'' (obligatoriu) – denumirea variabilei care va fi eliminată;\\ • ''scope'' – scopul variabilei care va fi eliminată. <code java> | + | |
| - | <c:remove var="myVariable" scope="application" /> | + | |
| - | </code> | | + | |
| - | | ''<c:catch>'' | gestionează orice obiect de tip ''Throwable'' întâlnit în corpul său, eventual expunându-l utilizatorului\\ \\ Atributul asociat etichetei este ''var'' ce indică denumirea variabilei care va stoca excepţia generată, în cazul în care va fi produsă de instrucţiunile din corpul său <code java> | + | |
| - | <c:catch var="${exception}"> | + | |
| - | <% some expression(s) that may potentially throw an error %> | + | |
| - | </c:catch> | + | |
| - | <c:out value="exception: ${exception.message}" /> | + | |
| - | </code> | | + | |
| - | | ''<c:if>'' | etichetă de tip condiţional, al cărui corp este evaluat în cazul când condiţia asociată este îndeplinită\\ \\ Atributele asociate etichetei sunt:\\ • ''test'' (obligatoriu) – condiţia care se doreşte a fi evaluată;\\ • ''var'' – denumirea variabilei care va stoca rezultatul condiţiei;\\ • ''scope'' – scopul variabilei care va stoca rezultatul condiţiei. <code java> | + | |
| - | <c:if test="${expression}" /> | + | |
| - | <% do some operations %> | + | |
| - | </c:if> | + | |
| - | </code> | | + | |
| - | | ''<c:choose>'' | etichetă de tip condiţional, indicând condiţii ce se exclud mutual, specificate prin ''<when>'' şi ''<otherwise>''; funcţionează ca instrucţiunea ''switch'' din Java, permiţând alegerea dintre mai multe alternative: ramurile case sunt specificate prin ''<when>'' în timp ce ramura default este specificată prin ''<otherwise>''.\\ \\ Eticheta nu are atribute. <code java> | + | |
| - | <c:choose> | + | |
| - | <c:when test="${expression1}"><% do some operations %></c:when> | + | |
| - | <c:when test="${expression2}"><% do some operations %></c:when> | + | |
| - | ... | + | |
| - | <c:when test="${expressionn}"> | + | |
| - | <% do some operations %> | + | |
| - | </c:when> | + | |
| - | <c:otherwise><% do some other operations %></c:otherwise> | + | |
| - | </c:choose /> | + | |
| - | </code> | | + | |
| - | | ''<c:when>'' | subetichetă a ''<choose>'', al cărui corp este evaluat în cazul când condiţia asociată este îndeplinită\\ Atributul asociat etichetei este test ce indică condiţia care se doreşte a fi evaluată. | | + | |
| - | | ''<c:otherwise>'' | subetichetă a ''<choose>'', ce urmează uneia sau mai multor etichete ''<when>'', determinând evaluarea corpului său, dacă nici una dintre condiţiile care o preced nu este îndeplinită.\\ \\ Eticheta nu are atribute. | | + | |
| - | | ''<c:import>'' | primeşte un URL relativ sau absolut şi expune conţinutul său întregii pagini, unui şir de caractere specificat în atributul var sau unui obiect Reader specificat în atributul ''varReader''.\\ Atributele asociate etichetei sunt:\\ • ''url'' (obligatoriu) – URL-ul ce trebuie obţinut şi inclus în pagină;\\ • ''context'' – caracterul / urmat de numele aplicaţiei web;\\ • ''charEncoding'' – setul de caractere utilizat pentru datele preluate;\\ • ''var'' – denumirea variabilei utilizată pentru a stoca textul preluat;\\ • ''scope'' – domeniul de vizibilitate al variabilei utilizată pentru a stoca textul preluat;\\ • ''varReader'' – denumirea unei variabile alternative pentru a expune conţinutul sub forma unui obiect ''java.io.Reader''; <code java> | + | |
| - | <c:import var="myVariabile" url="http://www.mysite.com" /> | + | |
| - | </code> | | + | |
| - | | ''<c:forEach>'' | etichetă de tip iteraţie, acceptând mai multe tipuri de colecţii, suportând parcurgerea submulţimilor; este o alternativă pentru instrucţiunile de iterare ''for'', ''while'' şi ''do-while'' din cadrul Java, care pot fi utilizate în cadrul unui scriplet\\ \\ Atributele asociate etichetei sunt:\\ • ''items'' – informaţia (colecţia) pe care se realizează iteraţia;\\ • ''begin'' – elementul colecţiei de la care se începe iteraţia;\\ • ''end'' – elementul colecţiei la care se termină iteraţia;\\ • ''step'' – pasul cu care se parcurg elementele colecţiei;\\ • ''var'' – denumirea variabilei care expune elementul curent;\\ • ''varStatus'' – denumirea variabilei care expune statutul iteraţiei; <code java> | + | |
| - | <c:forEach var="myVariabile" items="${myCollection}"> | + | |
| - | <c:out value="${myVariabile}" /><br /> | + | |
| - | </c:forEach> | + | |
| - | </code> | | + | |
| - | | ''<c:forTokens>'' | iterează asupra unor particule, separate prin delimitatori\\ \\ Suportă aceleaşi atribute ca şi <foreach>, la care se adaugă delim, care indică caracterele utilizate ca delimitatori <code java> | + | |
| - | <c:forTokens var="myVariabile" items="${myString}" delim=","> | + | |
| - | <c:out value="${myVariabile}" /><br /> | + | |
| - | </c:forTokens> | + | |
| - | </code> | | + | |
| - | | ''<c:param>'' | adaugă un parametru la URL-ul specificat în cadrul unei etichete ''<import>'', realizând şi codificarea acestuia\\ \\ Atributele asociate etichetei sunt:\\ • ''name'' – denumirea parametrului care va fi inclus în URL;\\ • ''value'' – valoarea parametrului care va fi inclus în URL; <code java> | + | |
| - | <c:import var="myVariabile" url="http://www.mysite.com"> | + | |
| - | <c:param name="param1Name" value="param1Value" /> | + | |
| - | <c:param name="param2Name" value="param2Value" /> | + | |
| - | ... | + | |
| - | <c:param name="paramnName" value="paramnValue" /> | + | |
| - | </c:import> | + | |
| - | </code> | | + | |
| - | | ''<c:redirect>'' | redirectează controlul la un alt URL, realizând suprascrierea sa în browser; suportă URL-uri relative la context şi eticheta ''<param>''\\ \\ Atributele asociate etichetei sunt:\\ • ''url'' (obligatoriu) – URL-ul la care se realizează redirectarea controlului;\\ • ''context'' – caracterul / urmat de numele aplicaţiei web. <code java> | + | |
| - | <c:redirect url="http://www.mysite.com"> | + | |
| - | </code> | | + | |
| - | | ''<c:url>'' | creează un URL cu parametrii de interogare opţionali, stocând valoarea acestuia într-o variabilă şi realizând rescrierea URL-ului dacă este necesar; este o alternativă la invocarea metodei ''response.encodeURL()'', suportând şi eticheta ''<param>''\\ \\ Atributele asociate etichetei sunt:\\ • ''value'' (obligatoriu) – URL-ul de bază;\\ • ''context'' – caracterul / urmat de numele aplicaţiei web;\\ • ''var'' – denumirea variabilei ce reţine URL-ul procesat;\\ • ''scope'' – domeniul de vizibilitate al variabilei ce reţine URL-ul procesat; <code java> | + | |
| - | <c:url value="http://www.mysite.com" /> | + | |
| - | </code> | | + | |
| - | + | ||
| - | ==== Etichete JSTL pentru formatare ==== | + | |
| - | + | ||
| - | Etichetele JSTL pentru formatare sunt utilizate pentru a formata şi afişa text, informaţii cu privire la dată calendaristică şi timp, numere precum şi site-uri Internet într-un format localizat. | + | |
| - | + | ||
| - | Pentru a putea referi aceste etichete, trebuie specificată locaţia de unde acestea pot fi încărcate. | + | |
| <code java> | <code java> | ||
| - | <%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> | + | myTable.put(myPut); |
| </code> | </code> | ||
| - | Cele mai frecvent utilizate etichete JSTL pentru formatare sunt: | + | Pentru a se citi date dintr-o tabelă, se foloseşte un obiect de tip ''Get'', care este instanţiat cu identificatorul (cheia de rând) a înregistrării care se doreşte obţinută, în format binar. |
| - | + | ||
| - | | ''<fmt:formatNumber>'' | utilizat pentru redarea unei valori numerice cu o anumită precizie sau format\\ \\ Atributele asociate etichetei sunt:\\ • ''value'' (obligatoriu) – valoarea numerică ce se doreşte afişată;\\ • ''type'' – poate lua valorile ''NUMBER'', ''CURRENCY'', ''PERCENT'';\\ • ''pattern'' – modelul de formatare pentru valoarea numerică ce se doreşte afişată; se folosesc codurile:\\ o ''0'' – reprezintă o cifră;\\ o ''E'' – reprezintă o formă exponenţială;\\ o ''#'' – reprezintă o cifră; se afişează 0 dacă lipseşte;\\ o ''.'' – separator pentru valorile zecimale;\\ o '','' – separator pentru grupurile reprezentând miile;\\ o '';'' – separator pentru formate;\\ o ''-'' – prefix pentru valori negative;\\ o ''%'' – afişează procentajul;\\ o ''?'' – afişează miimile;\\ o ''*'' – înlocuit de simbolul valutei curente;\\ o ''X'' – indică faptul că orice alt caracter poate fi folosit ca prefix sau sufix;\\ o ''''' – utilizat pentru a cita caractere speciale în prefix sau sufix.\\ • ''currencyCode'' – codul valutei (pentru tipul ''CURRENCY''), preluat din localizarea curentă;\\ • ''currencySymbol'' – simbolul valutei (pentru tipul ''CURRENCY''), preluat din localizarea curentă;\\ • ''groupingUsed'' – dacă se folosesc grupuri de numere; se foloseşte pentru a introduce '','' între grupurile care reprezintă miile;\\ • ''maxIntegerDigits'' – numărul maxim de cifre întregi care pot fi afişate; dacă valoarea este depăşită, atunci rezultatul se trunchează;\\ • ''minIntegerDigits'' – numărul minim de cifre întregi care pot fi afişate;\\ • ''maxFractionDigits'' – numărul maxim de zecimale care pot fi afişate; dacă valoarea este depăşită, atunci rezultatul se rotunjeşte;\\ • ''minFractionDigits'' – numărul minim de zecimale care pot fi afişate;\\ • ''var'' – denumirea variabilei care reţine valoarea formatată;\\ • ''scope'' – domeniul de vizibilitate al variabilei care reţine valoarea formatată. | | + | |
| - | | ''<fmt:parseNumber>'' | parsează reprezentarea sub formă de şir de caractere pentru o valoare numerică, o valută sau un procentaj\\ \\ Atributele asociate etichetei sunt:\\ • ''value'' – valoarea numerică ce se doreşte a fi parsată;\\ • ''type'' – poate lua valorile NUMBER, CURRENCY, PERCENT;\\ • ''parseLocale'' – localizarea folosită la parsarea numărului;\\ • ''integerOnly'' – indică parsarea de valori întregi sau reale;\\ • ''pattern'' – modelul de parsare;\\ • ''timeZone'' – zona de timp a datei calendaristice parsate;\\ • ''var'' – denumirea variabilei care reţine valoarea parsată;\\ • ''scope'' – domeniul de vizibilitate al variabilei care reţine valoarea parsată. | | + | |
| - | | ''<fmt:formatDate>'' | utilizat pentru formatarea unei date calendaristice / timp folosind stilurile şi modelele oferite\\ \\ Atributele asociate etichetei sunt:\\ • ''value'' (obligatoriu) – data calendaristică de afişat;\\ • ''type'' – poate lua valorile ''DATE'', ''TIME'', ''BOTH'';\\ • ''dateSytle'' – poate lua valorile ''FULL'', ''LONG'', ''MEDIUM'', ''SHORT'', ''DEFAULT'';\\ • ''timeStyle'' – poate lua valorile ''FULL'', ''LONG'', ''MEDIUM'', ''SHORT'', ''DEFAULT'';\\ • ''pattern'' – modelul de formatare pentru valoarea de tip dată calendaristică / timp ce se doreşte afişată; se folosesc codurile:\\ o ''G'' – era;\\ o ''y'' – anul;\\ o ''M'' – luna;\\ o ''d'' – ziua din lună;\\ o ''h'' – ora (format 12 ore);\\ o ''H'' – ora (format 24 ore);\\ o ''m'' – minutul;\\ o ''s'' – secunda;\\ o ''S'' – milisecunda;\\ o ''E'' – ziua din săptămână;\\ o ''D'' – ziua din an;\\ o ''F'' – ziua din săptămână în cadrul lunii;\\ o ''w'' – săptămâna din an;\\ o ''W'' – săptămâna din lună;\\ o ''a'' – indicatorul AM / PM;\\ o ''k'' – ora (format 12 ore);\\ o ''K'' – ora (format 24 ore);\\ o ''z'' – zona de timp;\\ o ''''' – caracter escape pentru informaţii de tip text;\\ o ''"'' – utilizat pentru citări;\\ • ''timeZone'' – zona de timp a datei calendaristice afişate;\\ • ''var'' – denumirea variabilei care reţine valoarea formatată;\\ • ''scope'' – domeniul de vizibilitate al variabilei care reţine valoarea formatată. | | + | |
| - | | ''<fmt:parseDate>'' | parsează reprezentarea sub formă de şir de caractere pentru o valoare de tip dată calendaristică / timp\\ \\ Are aceleaşi atribute ca ''<formatDate>'' la care se adaugă ''parseLocale'', indicând localizarea care trebuie utilizată atunci când se parsează informaţia de tip dată calendaristică. | | + | |
| - | | ''<fmt:bundle>'' | încarcă o resursă ce urmează să fie utilizată în corpul său, de eticheta ''<message>''\\ \\ Atributele asociate etichetei sunt:\\ • ''basename'' (obligatoriu) – specifică numele de bază pentru resursa care se doreşte a fi încărcată;\\ • ''prefix'' – valoarea care va preceda fiecare denumire de cheie în subeticheta ''<message>''. <code java> | + | |
| - | <fmt:bundle basename="myBaseName"> | + | |
| - | <fmt:message key="myPrefix.myKey" /> | + | |
| - | </fmt:bundle> | + | |
| - | echivalent cu | + | |
| - | <fmt:bundle basename="myBaseName" prefix="myPrefix"> | + | |
| - | <fmt:message key="myKey" /> | + | |
| - | </fmt:bundle> | + | |
| - | </code> | | + | |
| - | | ''<fmt:setLocale>'' | stochează localizarea dată în variabila de configurare referitoare la localizare\\ \\ Atributele asociate etichetei sunt:\\ • ''value'' (obligatoriu) – specifică un cod format din două părţi indicând codul de limbă (ISO-639) şi codul de ţară (ISO-3166);\\ • ''variant'' – variantă specifică browser-ului;\\ • ''scope'' – domeniul de vizibilitate al variabilei de configurare referitoare la localizare. | | + | |
| - | | ''<fmt:setBundle>'' | încarcă o resursă pe care o stochează într-o variabilă având un domeniu de vizibilitate, respectiv în variabila de configurare referitoare la resurse\\ \\ Atributele asociate etichetei sunt:\\ • ''basename'' (obligatoriu) – specifică numele de bază pentru familia de resurse pentru a fi expusă ca o variabilă având un anumit domeniu de vizibilitate, respectiv ca o variabilă de configurare;\\ • ''var'' – denumirea variabilei care reţine valoarea resursei;\\ • ''scope'' – domeniul de vizibilitate al variabilei care reţine valoarea resursei. | | + | |
| - | | ''<fmt:timeZone>'' | specifică zona de timp pentru orice tip de acţiune vizând formatarea sau parsarea din corpul său.\\ \\ Atributul asociat etichetei este value ce indică zona de timp care va fi aplicată corpului său. | | + | |
| - | | ''<fmt:setTimeZone>'' | stochează zona de timp dată în variabila de configurare referitoare la zona de timp\\ \\ Atributele asociate etichetei sunt:\\ • ''value'' (obligatoriu) – zona de timp care va fi expusă ca o variabilă având un anumit domeniu de vizibilitate, respectiv ca o variabilă de configurare;\\ • ''var'' – denumirea variabilei care reţine zona de timp;\\ • ''scope'' – domeniul de vizibilitate al variabilei care reţine zona de timp. | | + | |
| - | | ''<fmt:message>'' | afişează un mesaj folosind un format localizat\\ \\ Atributele asociate etichetei sunt:\\ • ''key'' – cheia mesajului care se doreşte a fi obţinut;\\ • ''bundle'' – identificatorul resursei care se doreşte a fi folosit;\\ • ''var'' – denumirea variabilei care reţine valoarea mesajului localizat;\\ • ''scope'' – domeniul de vizibilitate al variabilei care reţine valoarea mesajului localizat. | | + | |
| - | | ''<fmt:requestEncoding>'' | stabileşte mecanismul de configurare al caracterelor pentru cerere\\ Atributul asociat etichetei este ''key'' prin care se indică mecanismul de configurare al caracterelor care va fi aplicat la decodificarea parametrilor din obiectul ''request''. | | + | |
| - | + | ||
| - | ==== Etichete JSTL pentru gestiunea informațiilor dintr-o bază de date ==== | + | |
| - | + | ||
| - | Etichetele JSTL pentru gestiunea informaţiilor într-o bază de date SQL oferă funcţionalitatea pentru interacţiunea cu baze de date relaţionale ca Oracle, MySQL sau Microsoft SQL Server. | + | |
| - | + | ||
| - | Pentru a putea referi aceste etichete, trebuie specificată locaţia de unde acestea pot fi încărcate. | + | |
| <code java> | <code java> | ||
| - | <%@ taglib prefix="sql" uri="http://java.sun.com/jsp/jstl/sql" %> | + | Get myGet = new Get(Bytes.toBytes("myRow")); |
| </code> | </code> | ||
| - | Cele mai frecvent utilizate etichete JSTL pentru gestiunea informaţiilor dintr-o bază de date sunt: | + | Un astfel de obiect implementează metoda ''add()'', prin care se indică coordonatele celulei la care se doreşte să se scrie valoarea în cauză. Metoda ''add()'' permite specificarea mai multor familii de coloane şi a mai multor coloane. |
| - | + | ||
| - | | ''<sql:setDataSource>'' | creează un obiect ''DataSource'' adecvat numai operaţiilor de prototipare\\ \\ Atributele asociate etichetei sunt:\\ • ''driver'' – numele clasei corespunzătoare "driver"-ului JDBC care trebuie înregistrată;\\ • ''url'' – URL-ul pentru realizarea conexiunii la baza de date;\\ • ''user'' – numele de utilizator folosit în mecanismul de autentificare pentru baza de date;\\ • ''password'' – parola folosită în mecanismul de autentificare pentru baza de date;\\ • ''dataSource'' – baza de date pregătită anterior;\\ • ''var'' – denumirea variabilei care va reprezenta baza de date;\\ • ''scope'' – domeniul de vizibilitate al variabile care va reprezenta baza de date. <code java> | + | |
| - | <sql:setDataSource var="connection" url="${dataBaseConnection}" | + | |
| - | user="${dataBaseUser}" password="${dataBasePassword}" /> | + | |
| - | </code> | | + | |
| - | | ''<sql:query>'' | execută interogarea SQL de tip ''SELECT'' definită în cadrul corpului folosind atributul sql, reţinând rezultatul într-o variabilă având un anumit domeniu de vizibilitate\\ \\ Atributele asociate etichetei sunt:\\ • ''sql'' – comanda SQL care va fi executată (ar trebui să întoarcă un obiect de tip ''ResultSet'');\\ • ''dataSource'' – conexiunea la baza de date care va fi utilizată (suprascrie valoarea implicită);\\ • ''maxRows'' – numărul maxim de rezultate care vor fi stocate în cadrul variabilei;\\ • ''startRow'' – numărul înregistrării (din cadrul rezultatului) de la care va începe stocarea;\\ • ''var'' – denumirea variabilei care va reține rezultatul;\\ • ''scope'' – domeniul de vizibilitate al variabilei care va reține rezultatul. <code java> | + | |
| - | <sql:query sql="${myQuery}" var="result" /> | + | |
| - | </code> | | + | |
| - | | ''<sql:update>'' | execută instrucţiunea SQL de actualizare a informaţiilor (de tip ''INSERT'', ''UPDATE'', ''DELETE'') – care nu întoarce date, definită în cadrul corpului folosind atributul sql\\ \\ Atributele asociate etichetei sunt:\\ • ''sql'' – comanda SQL care va fi executată (ar trebui să NU întoarcă un obiect de tip ''ResultSet'');\\ • ''dataSource'' – conexiunea la baza de date care va fi utilizată (suprascrie valoarea implicită);\\ • ''var'' – denumirea variabilei care va stoca numărul de înregistrări afectate de instrucţiune;\\ • ''scope'' – domeniul de vizibilitate al variabilei care va stoca numărul de înregistrări afectate de instrucţiune. <code java> | + | |
| - | <sql:update dataSource="${connection}" var="count"> | + | |
| - | <%-- some INSERT, UPDATE or DELETE query --> | + | |
| - | </sql:update> | + | |
| - | </code> | | + | |
| - | | ''<sql:param>'' | stabileşte valoarea unui parametru al unei instrucţiuni SQL, fiind folosit de obicei împreună cu etichetele ''<query>'' sau ''<update>''\\ \\ Atributul asociat etichetei este value prin care se indică valoarea care se doreşte a fi asociată parametrului. <code java> | + | |
| - | <sql:query dataSource="${connection}" var="result"> | + | |
| - | SELECT FROM table WHERE attribute = ? | + | |
| - | <sql:param value="${missingTableAttributeValue}" /> | + | |
| - | </sql:query> | + | |
| - | </code> <code java> | + | |
| - | <sql:update dataSource="${connection}" var="count"> | + | |
| - | <%-- some INSERT, UPDATE or DELETE query with one or more | + | |
| - | missing attribute values --> | + | |
| - | <sql:param value="${missingTableAttributeValue}" /> | + | |
| - | </sql:update> | + | |
| - | </code> | | + | |
| - | | ''<sql:dateParam>'' | stabileşte valoarea unui parametru al unei instrucţiuni SQL având tipul ''java.util.Date''\\ \\ Atributele asociate etichetei sunt:\\ • ''value'' – valoarea parametrului având tipul ''java.util.Date'', care trebuie stocat;\\ • ''type'' – poate avea valorile ''DATE'' (doar date caledaristice), ''TIME'' (doar timp) sau ''TIMESTAMP'' (dată calendaristică şi timp).\\ Utilizarea sa este similară cu a etichete ''<param>''. | | + | |
| - | | ''<sql:transaction>'' | oferă elemente care realizează acţiuni folosind un obiect ''Connection'' partajat, executate în cadrul unei tranzacţii; grupează interogări exprimate de etichetele ''<query>'' şi ''<update>'' într-o tranzacţie, asigurând că efectul acestora este fie unitar asupra bazei de date, menţinându-se starea sa iniţială în cazul producerii unei erori.\\ \\ Atributele asociate etichetei sunt:\\ • ''dataSource'' – conexiunea la baza de date care va fi utilizată (suprascrie valoarea implicită);\\ • ''isolation'' – poate avea valorile ''READ_COMMITED'', ''READ_UNCOMMMITTED'', ''REPEATABLE_READ'' sau ''SERIALIZABLE''. <code java> | + | |
| - | <sql:transaction dataSource="${connection}"> | + | |
| - | <sql:update var="count1"> | + | |
| - | <%-- some INSERT queries --> | + | |
| - | </sql:update> | + | |
| - | <sql:update var="count2"> | + | |
| - | <%-- some UPDATE queries --> | + | |
| - | </sql:update> | + | |
| - | <sql:update var="count3"> | + | |
| - | <%-- some DELETE queries --> | + | |
| - | </sql:update> | + | |
| - | </sql:transaction> | + | |
| - | </code> | | + | |
| - | + | ||
| - | ==== Etichete JSTL pentru gestiunea documentelor XML ==== | + | |
| - | + | ||
| - | Etichetele JSTL pentru gestiunea documentelor XML oferă posibilitatea de a manipula acest tip de resurse, implementând operaţii precum parsarea şi transformarea acestora precum şi controlul fluxului bazat pe expresii XPath. | + | |
| - | + | ||
| - | Pentru a putea referi aceste etichete, trebuie specificată locaţia de unde acestea pot fi încărcate. | + | |
| <code java> | <code java> | ||
| - | <%@ taglib prefix="x" uri="http://java.sun.com/jsp/jstl/xml" %> | + | Get.addFamily(family); |
| + | Get.addColumn(family, column); | ||
| </code> | </code> | ||
| - | Cele mai frecvent utilizate etichete JSTL pentru gestiunea documentelor XML sunt: | + | Este foarte important să fie indicate exact datele necesare în cadrul aplicaţiei, altfel fiind returnat un rând întreg. |
| - | | ''<x:out>'' | evaluează valoarea unei expresii XPath pe care o afişează\\ \\ Atributele asociate etichetei sunt:\\ • ''select'' (obligatoriu) – expresia XPath care trebuie evaluată, adeseori folosind variabile XPath;\\ • ''escapeXml'' – indică dacă caracterele speciale XML ar trebui ignorate | | + | Filtrarea se poate realiza prin specificarea unui interval de timp, respectiv al unui număr de versiuni (prin metodele ''setTimeRange()'' şi ''setMaxVersions()''). |
| - | | ''<x:parse>'' | utilizat pentru a parsa date XML specificate fie prin intermediul unui atribut, fie în corpul etichetei\\ \\ Atributele asociate etichetei sunt:\\ • ''var'' – variabila care conţine datele XML parsate;\\ • ''xml'' – textul documentului care se doreşte a fi parsat (de tip ''String'' sau ''Reader'');\\ • ''systemId'' – URI-ul identificatorului de sistem pentru a parsa documentul;\\ • ''filter'' – filtrul ce trebuie aplicat documentului sursă;\\ • ''doc'' – documentul XML ce se doreşte a fi parsat;\\ • ''scope'' – domeniul de vizibilitate al variabilei specificate de atributul ''var'';\\ • ''varDom'' – variabila care conţine datele XML parsate;\\ • ''scopeDom'' – domeniul de vizibilitate al variabilei specificate de atributul ''varDom''. <code java> | + | |
| - | <x:parse xml="${myXmlDocument}" var="variable" /> | + | |
| - | <x:out select="$variable/root/element[index]/attribute" /> | + | |
| - | </code> | | + | |
| - | | ''<x:set>'' | atribuie unei variabile valoarea unei expresii XPath; în funcţie de tipul rezultat la evaluarea expresiei XPath, vor fi create obiecte având tipul ''java.lang.Boolean'', ''java.lang.String'', ''java.lang.Number''.\\ \\ Atributele asociate etichetei sunt:\\ • ''var'' (obligatoriu) – variabila care va reţine valoarea expresiei XPath;\\ • ''select'' – expresia XPath care se doreşte evaluată;\\ • ''scope'' – domeniul de vizibilitate al variabilei specificate de atributul ''var''. <code java> | + | |
| - | <x:parse xml="${myXmlDocument}" var="variable1" /> | + | |
| - | <x:set var="variable2" select="$variable1/root/element" /> | + | |
| - | </code> | | + | |
| - | | ''<x:if>'' | evaluează o condiţie ce are forma unei expresii XPath şi dacă aceasta se verifică se procesează corpul etichetei\\ \\ Atributele asociate etichetei sunt:\\ • ''select'' (obligatoriu) – expresia XPath exprimând condiţia ce se doreşte evaluată;\\ • ''var'' – variabila care va reţine rezultatul evaluării condiţiei;\\ • ''scope'' – domeniul de vizibilitate al variabilei specificate de atributul ''var''. <code java> | + | |
| - | <x:parse xml="${myXmlDocument}" var="variable" /> | + | |
| - | <x:if select="$variable/root/element[index]/attribute != 0" /> | + | |
| - | The attribute of the element at index is not null! | + | |
| - | </x:if> | + | |
| - | </code> | | + | |
| - | | ''<x:forEach>'' | iterează asupra nodurilor unui document XML\\ \\ Atributele asociate etichetei sunt:\\ • ''select'' (obligatoriu) – expresia XPath care va fi evaluată;\\ • ''var'' – denumirea variabilei care va reţine valoarea elementului curent pentru fiecare iteraţie;\\ • ''begin'' – indexul de început pentru iteraţie;\\ • ''end'' – indexul de sfârşit pentru iteraţie;\\ • ''step'' – valoarea cu care va fi incrementat indexul pe parcursul incrementării colecţiei;\\ • ''varStatus'' – denumirea variabilei în care se va reţine statutul iteraţiei <code java> | + | |
| - | <x:parse xml="${myXmlDocument}" var="variable" /> | + | |
| - | <x:forEach select="$variable/root/element/attribute" var="item" /> | + | |
| - | <x:out select="$item" /> | + | |
| - | </x:forEach> | + | |
| - | </code> | | + | |
| - | | ''<x:choose>'' | etichetă de tip condiţional care stabileşte un context pentru operaţii condiţionale care se exclud mutual, marcate prin ''<when>'' şi ''<otherwise>''\\ \\ Eticheta nu are atribute. <code java> | + | |
| - | <x:parse xml="${myXmlDocument}" var="variable" /> | + | |
| - | <x:choose> | + | |
| - | <x:when select="$variable//element/attribute eq value1"> | + | |
| - | Attribute has value 1! | + | |
| - | </x:when> | + | |
| - | <x:when select="$variable//element/attribute eq value2"> | + | |
| - | Attribute has value 2! | + | |
| - | </x:when> | + | |
| - | ... | + | |
| - | <x:when select="$variable//element/attribute eq valuen"> | + | |
| - | Attribute has value n! | + | |
| - | </x:when> | + | |
| - | <x:otherwise> | + | |
| - | Attribute has unknown value! | + | |
| - | </x:otherwise> | + | |
| - | </x:choose> | + | |
| - | </code> | | + | |
| - | | ''<x:when>'' | subetichetă a etichetei ''<choose>'' al cărei corp este inclus dacă se verifică condiţia asociată.\\ \\ Eticheta are un singur atribut, ''select'', care conţine condiţia ce se doreşte a fi evaluată. | | + | |
| - | | ''<x:otherwise>'' | subetichetă a etichetei ''<choose>'' ce urmează uneia sau mai multor etichete ''<when>'', determinând evaluarea corpului său, dacă nici una dintre condiţiile care o preced nu este îndeplinită.\\ \\ Eticheta nu are atribute. | | + | |
| - | | ''<x:transform>'' | se aplică la transformare XSL a unui document XML\\ \\ Atributele asociate etichetei sunt:\\ • ''doc'' – documentul XML sursă pentru transformarea XSLT;\\ • ''docSystemId'' – URI-ul documentului XML original;\\ • ''xslt'' (obligatoriu) – foaia de stil XSLT care conţine instrucţiunile pentru transformare;\\ • ''xsltSystemId'' – URI-ul documentului XSLT original;\\ • ''result'' – obiectul rezultat care va accepta rezultatul transformării;\\ • ''var'' – denumirea variabilei în care se va reţine rezultatul transformării\\ • ''scope'' – domeniul de vizibilitate ce va expune rezultatul transformării. <code java> | + | |
| - | <x:parse xml="${myXmlDocument}" var="variable" /> | + | |
| - | <c:import url="http://www.mysite.com/document.xsl" var="xslt" /> | + | |
| - | <x:transform doc="${variable}" xslt="${xslt}" /> | + | |
| - | </code> | | + | |
| - | | ''<x:param>'' | utilizat împreună cu eticheta de transformare pentru a stabili un parametru în foaia de stil XSLT\\ \\ Atributele asociate etichetei sunt:\\ • ''name'' – denumirea parametrului XSLT ce se doreşte a fi stabilit;\\ • ''value'' – valoarea parametrului XSLT ce se doreşte a fi stabilit. <code java> | + | |
| - | <x:transform doc="${variable}" xslt="${xslt}" /> | + | |
| - | <x:param name="attributeName" value="attributeValue" /> | + | |
| - | </x:transform> | + | |
| - | </code> | | + | |
| - | ==== Funcții JSTL ==== | + | Transferul propriu-zis al datelor se realizează la apelul metodei ''get()'', pe instanţa ''HTable'': |
| - | + | ||
| - | Expresion Language permite folosirea de funcţii în cadrul expresiilor, acestea trebuind să fie definite în cadrul unor biblioteci de etichete predefinite, apelul său făcându-se folosind sintaxa: | + | |
| <code java> | <code java> | ||
| - | ${ns:function(param1, param2, ..., paramn)} | + | Result myResult = myTable.get(myGet); |
| </code> | </code> | ||
| - | JSTL include un număr de funcţii standard, dintre care numeroase implică manipularea şirurilor de caractere. | + | Obţinerea rezultatelor dintr-un obiect de tip ''Result'' se face prin metoda ''getValue()'', specificându-se denumirea familiei de coloane şi a coloanei. Alte metode sunt ''getRow()'' pentru obţinerea identificatorului unei înregistrări, ''isEmpty()'' pentru a verifica dacă rezultatul este vid, ''size()'' pentru a obţine numărul de celule şi ''containsColumn(family:column)'' pentru a verifica existenţa unei coloane. |
| - | Pentru a putea referi aceste etichete, trebuie specificată locaţia de unde acestea pot fi încărcate. | + | |
| + | Un obiect ''HTable'' trebuie întotdeauna închis dacă nu mai este necesar sau atunci când s-a produs o excepţie (fiind plasat de regulă pe ramura ''catch'', operaţiile pe tabela respectivă fiind plasate pe ramura ''try''): | ||
| <code java> | <code java> | ||
| - | <%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %> | + | myTable.close(); |
| </code> | </code> | ||
| - | Lista de funcţii standard JSTL este redată în continuare: | + | ===== Activitate de Laborator ===== |
| - | | ''fn:contains()'' | verifică dacă şirul de caractere conţine un alt subşir; primeşte doi parametrii de tip ''java.lang.String'' având semnificaţia şirului de caractere, respectiv a subşirului; rezultatul întors este de tip ''boolean''. <code java> | + | **0**. Să se cloneze în directorul de pe discul local conținutul depozitului la distanță de la [[https://www.github.com/aipi2015/Laborator08]]. În urma acestei operații, directorul Laborator08 va trebui să conțină subdirectorul ''labtasks'', fișierele ''README.md'' și ''LICENSE''. |
| - | <c:set var="variable" value="${fn:contains(stringToBeSearched, substringToSearch)}" /> | + | |
| - | </code> | | + | |
| - | | ''fn:containsIgnoreCase()'' | verifică dacă şirul de caractere conţine un alt subşir, ignorând capitalizarea; primeşte doi parametrii de tip ''java.lang.String'' având semnificaţia şirului de caractere, respectiv a subşirului; rezultatul întors este de tip ''boolean''. <code java> | + | |
| - | <c:set var="variable" value="${fn:containsIgnoreCase(stringToBeSearched, substringToSearch)}" /> | + | |
| - | </code> | | + | |
| - | | ''fn:endsWith()'' | verifică dacă şirul de caractere are un anumit sufix; primeşte doi parametrii de tip ''java.lang.String'' având semnificaţia şirului de caractere, respectiv a sufixului; rezultatul întors este de tip ''boolean''. <code java> | + | |
| - | <c:set var="variable" value="${fn:endsWith(stringToBeSearched, sufix)}" /> | + | |
| - | </code> | | + | |
| - | | ''fn:escapeXml()'' | transformă caracterele care ar putea fi interpretate ca adnotări XML la valoarea corespunzătoare; primeşte un parametru de tip ''java.lang.String'' având semnificaţia şirului de caractere care se doreşte a fi transformat; rezultatul întors este de tip ''java.lang.String''. <code java> | + | |
| - | <c:set var="variable" value="${fn:escapeXml(stringToBeXmlEscaped)}" /> | + | |
| - | </code> | | + | |
| - | | ''fn:indexOf()'' | întoarce prima apariţie a unui subşir specificat în cadrul şirului de caractere; primeşte doi parametrii de tip ''java.lang.String'' având semnificaţia şirului de caractere în care se realizează căutarea, respectiv a şirului care este căutat; rezultatul întors este de tip ''int'' (dacă şirul nu este găsit se întoarce valoarea -1) <code java> | + | |
| - | <c:set var="variable" value="${fn:indexOf(stringToBeSearched, substringToSearch)}" /> | + | |
| - | </code> | | + | |
| - | | ''fn:join()'' | concatenează toate elementele unui vector într-un singur şir de caractere; primeşte doi parametrii, unul de tip ''java.lang.String[]'' conţinând elementele vectorului şi unul de tip ''java.lang.String'', reprezentând separatorul; întoarce un rezultat de tip ''java.lang.String'' <code java> | + | |
| - | <c:set var="variable" value="${fn:join(arrayToBeJoined, separator)}" /> | + | |
| - | </code> | | + | |
| - | | ''fn:length()'' | întoarce numărul de elemente al colecţiei sau dimensiunea şirului de caractere; primeşte un parametru de tip ''java.lang.Object'' (de tip colecţie sau şir de caractere); întoarce un rezultat de tip ''int''. <code java> | + | |
| - | <c:set var="variable1" value="${fn:length(myList)}" /> | + | |
| - | <c:set var="variable2" value="${fn:length(myString)}" /> | + | |
| - | </code> | | + | |
| - | | ''fn:replace()'' | întoarce un şir de caractere obţinut prin înlocuirea unei secvenţe din cadrul său cu o altă secvenţă; primeşte trei parametrii de tip ''java.lang.String'', reprezentând şirul de caractere în care se realizează înlocuirea, secvenţa care se doreşte înlocuită şi valoarea cu care aceasta este înlocuită; întoarce un rezultat de tip ''boolean'' <code java> | + | |
| - | <c:set var="variable" value="${fn:length(myString, sequenceToReplace, sequenceToReplaceWith)}" /> | + | |
| - | </code> | | + | |
| - | | ''fn:split()'' | împarte şirul de caractere într-un vector de subşiruri primeşte doi parametrii de tip ''java.lang.String'', reprezentând şirul de caractere care va fi împărţit, respectiv delimitatorii pe baza cărora se va realiza această operaţie; întoarce un rezultat de tip ''java.lang.String[]''. <code java> | + | |
| - | <c:set var="variable" value="${fn:split(myString, delimiter)}" /> | + | |
| - | </code> | | + | |
| - | | ''fn:startsWith()'' | verifică dacă şirul de caractere are un anumit prefix; primeşte doi parametrii de tip ''java.lang.String'' având semnificaţia şirului de caractere, respectiv a prefixului; rezultatul întors este de tip ''boolean''. <code java> | + | |
| - | <c:set var="variable" value="${fn:startsWith(stringToBeSearched, prefix)}" /> | + | |
| - | </code> | | + | |
| - | | ''fn:substring()'' | întoarce un subşir al şirului de caractere; primeşte un parametru de tip ''java.lang.String'' reprezentând şirul din care se determină subşirul şi doi parametrii de tip int reprezentând poziţiile de început şi de sfârşit; rezultatul întors este de tip ''java.lang.String''. <code java> | + | |
| - | <c:set var="variable" value="${fn:substring(myString, startIndex, endIndex)}" /> | + | |
| - | </code> | | + | |
| - | | ''fn:substringAfter()'' | întoarce un subşir al şirului de caractere ce succede unei anumite secvenţe; primeşte doi parametrii de tip ''java.lang.String'' având semnificaţia şirului de caractere din care se determină subşirul, respectiv secvenţa după care urmează subşirul respectiv; rezultatul întors este de tip ''java.lang.String''. <code java> | + | |
| - | <c:set var="variable" value="${fn:substringAfter(myString, subSequence)}" /> | + | |
| - | </code> | | + | |
| - | | ''fn:substringBefore()'' | întoarce un subşir al şirului de caractere ce precede o anumită secvenţă; primeşte doi parametrii de tip ''java.lang.String'' având semnificaţia şirului de caractere din care se determină subşirul, respectiv secvenţa înainte de care se găseşte subşirul respectiv; rezultatul întors este de tip ''java.lang.String''. <code java> | + | |
| - | <c:set var="variable" value="${fn:substringBefore(myString, subSequence)}" /> | + | |
| - | </code> | | + | |
| - | | ''fn:toLowerCase()'' | transformă toate caracterele şirului în minuscule; primeşte un caracter de tip ''java.lang.String'' reprezentând şirul de caractere ce se doreşte a fi transformat; rezultatul întors este de tip ''java.lang.String'' <code java> | + | |
| - | <c:set var="variable" value="${fn:toLowerCase(myString)}" /> | + | |
| - | </code> | | + | |
| - | | ''fn:toUpperCase()'' | transformă toate caracterele şirului în majuscule; primeşte un caracter de tip ''java.lang.String'' reprezentând şirul de caractere ce se doreşte a fi transformat; rezultatul întors este de tip ''java.lang.String'' <code java> | + | |
| - | <c:set var="variable" value="${fn:toUpperCase(myString)}" /> | + | |
| - | </code> | | + | |
| - | | ''fn:trim()'' | elimină spaţiile care se găsesc la începutul şi sfârşitul şirului de caractere; primeşte un caracter de tip ''java.lang.String'' reprezentând şirul de caractere care se doreşte a fi transformat; rezultatul întors este de ''tip java.lang.String'' <code java> | + | |
| - | <c:set var="variable" value="${fn:trim(myString)}" /> | + | |
| - | </code> | | + | |
| - | ==== Etichete definite de utilizator ==== | + | <code> |
| + | student@aipi2015:~$ git clone https://www.github.com/aipi2015/Laborator08.git | ||
| + | </code> | ||
| - | În JavaServer Pages pot fi implementate etichete definite de utilizator, care în momentul transformării servlet-ului corespunzător sunt convertite în operaţiile specificate de clasa care îi este asociată, pentru ca atunci când se accesează pagina respectivă, acestea să fie executate. O clasă care defineşte funcţionalitatea unei anumite etichete trebuie să fie derivată din ''javax.servlet.jsp.tagtext.SimpleTagSupport'', implementând metoda ''doTag()''. Aceasta poate accesa obiectele din cadrul paginii JSP prin intermediul metodei ''getJSPContext()''. De asemenea, în cazul în care în corpul etichetei se specifică anumită valoare, aceasta poate fi obţinută într-un obiect de tip ''StringWriter'' prin metoda ''getJspBody().invoke()''. | + | <note tip>În cadrul acestui laborator, este necesar să se foloseasă [[:resurse:masina_virtuala|masina virtuală Ubuntu 14.04]] care conține instalate Hadoop 2.6.1, HBase 1.1.2 precum și un plugin pentru mediul integrat de dezvoltare Eclipse, prin intermediul căruia pot fi dezvoltate aplicațiile distribuite folosind aceste tehnologii. </note> |
| - | <file java CustomTag.java> | + | **1.** Să se pornească maşina virtuală [[http://aipi2015.andreirosucojocaru.ro/Aipi2015.zip|Ubuntu 14.04]], autentificarea făcându-se folosind credențialele aipi2015 (ca nume de utilizator) şi StudentAipi2015 (ca parolă). |
| - | import java.io.*; | + | |
| - | import javax.servlet.jsp.*; | + | |
| - | import javax.servlet.jsp.tagext.*; | + | |
| - | public class CustomTag extends SimpleTagSupport { | + | **2.** Într-un terminal, să se pornească sistemul de fişiere distribuite HDFS şi sistemul de gestiune al resurselor YARN. |
| - | + | ||
| - | private String customAttribute; | + | |
| - | public void setCustomAttribute(String customAttribute) { | + | |
| - | this. customAttribute = customAttribute; | + | |
| - | } | + | |
| - | public String getCustomAttribute() { | + | |
| - | return customAttribute; | + | |
| - | } | + | |
| - | + | ||
| - | StringWriter stringWriter = new StringWriter(); | + | |
| - | public void doTag() throws JspException, IOException { | + | |
| - | if (customAttribute != null) { | + | |
| - | // process tag attribute | + | |
| - | getJspContext().getOut().println(customAttribute); | + | |
| - | } else { | + | |
| - | // get the content from the tag body | + | |
| - | getJspBody().invoke(stringWriter); | + | |
| - | // process tag body’s content | + | |
| - | getJspContext().getOut().println(stringWriter.toString()); | + | |
| - | } | + | |
| - | } | + | |
| - | } | + | <code> |
| - | </file> | + | student@aipi2015:~$ start-dfs.sh |
| + | student@aipi2015:~$ start-yarn.sh | ||
| + | </code> | ||
| - | Clasa asociată etichetei definite de utilizator va trebui plasată la o locaţie vizibilă prin variabila de mediu ''CLASSPATH''. | + | <note important>Verificaţi faptul că au pornit corect prin inspectarea interfeţelor web de la adresele [[http://localhost:50070]], respectiv [[http://localhost:8088]].</note> |
| - | Asocierea dintre etichetă şi clasa asociată se face într-un fişier ''custom.tld'' care va fi plasat în ''webapps/root/WEB-INF'' al serverului web Apache Tomcat 8.x. Astfel, se va specifica numele etichetei (aşa cum va fi specificat în pagina JSP – elementul ''<name>'', denumirea clasei care îi implementează funcţionalitatea (elementul ''<tag-class>'') precum şi tipul de conţinut al corpului etichetei (elementul ''<body-content>'', având ca valori posibile ''empty'', dacă trebuie să fie vid, ''scriptless'' dacă este acceptat numai text static, expresii EL sau alte etichete şi ''tagdependent'' în cazul în care conţinutul este scris în alt limbaj, fiind interpretat de implementarea etichetei). De asemenea, pentru fiecare atribut trebuie indicate proprietăţile ''name'' (identificator unic al atributului în cadrul etichetei), ''required'' (care distinge între atributele obligatorii şi cele opţionale), ''rtexprvalue'' (care stabileşte validitatea valorii unei expresii definită în momentul rulării pentru atributul etichetei), ''type'' (care specifică tipul Java al atributului respectiv, implicit fiind ''java.lang.String''), ''description'' şi ''fragment'' (care indică dacă valoarea asociată atributului va fi tratată ca un ''JspFragment''). | + | **3.** Într-un terminal, să se pornească o instanţă a sistemului de gestiune pentru baze distribuite HBase. |
| - | <code xml> | + | <code> |
| - | <taglib> | + | student@aipi2015:~$ start-hbase.sh |
| - | <tlib-version>1.0</tlib-version> | + | |
| - | <jsp-version>2.0</jsp-version> | + | |
| - | <short-name>A short description of my custom tag</short-name> | + | |
| - | <tag> | + | |
| - | <name>CustomTag</name> | + | |
| - | <tag-class>mypackage.CustomTag</tag-class> | + | |
| - | <body-content>scriptless</body-content> | + | |
| - | <attribute> | + | |
| - | <name>customAttribute</name> | + | |
| - | <required>true</true> | + | |
| - | <type>java.lang.Integer</type> | + | |
| - | <fragment>false</fragment> | + | |
| - | </attribute> | + | |
| - | </tag> | + | |
| - | </taglib> | + | |
| </code> | </code> | ||
| - | Pentru a putea fi folosită în cadrul unei pagini JSP, eticheta definită de utilizator trebuie declarată în acelaşi mod în care se procedează şi cu etichetele standard JSTL: | + | <note important>Verificaţi faptul că rulează toate procesele Hadoop / HBase rulând comanda |
| - | + | <code> | |
| - | <code java> | + | student@aipi2015:~$ jps |
| - | <%@ taglib prefix="customPrefix" uri="WEB-INF/custom.tld"%> | + | |
| </code> | </code> | ||
| + | În execuție trebuie să se afle procesele Java NameNode, SecondaryNameNode, DataNode (ale sistemului distribuit de fișiere HDFS), ResourceManager, NodeManager (ale sistemului de gestiune a resurselor YARN), respectiv HRegionServer, HMaster și HQuorumPeer (ale sistemului de gestiune pentru baze de date distribuite HBase). | ||
| + | </note> | ||
| - | În momentul în care este utilizată, eticheta va avea numele definit în fişierul ''custom.tld'', fiind prefixată cu denumirea indicată la declararea ei, precizând valori pentru toate atributele care au proprietatea ''required'': | + | **4.** Să se ruleze script-urile HBase pentru instalarea bazei de date. Vor trebui rulate scripturile ''Laborator08_DDLc.rb'' şi ''Laborator08_DML.rb'', localizate în directorul ''scripts''. |
| - | + | ||
| - | <code java> | + | |
| - | ... | + | |
| - | <customPrefix:CustomTag customAttribute="0"> | + | |
| - | Some custom tag body | + | |
| - | </customPrefix:CustomTag> | + | |
| - | ... | + | |
| - | </code> | + | |
| - | + | ||
| - | Potrivit implementării clasei asociate etichetei, efectul utilizării sale va fi afişarea valorii atributului specificat, în situaţia în care nu se decide realizarea altor tipuri de procesări asupra ei. | + | |
| - | + | ||
| - | Se pot defini mai multe biblioteci de etichete definite de utilizator, grupate în funcţie de tipul de funcţionalitate pe care o pun la dispoziţia programatorilor, fiecare dintre acestea regăsindu-se într-un alt fişier şi utilizându-se un prefix diferit. | + | |
| - | + | ||
| - | ===== Activitate de Laborator ===== | + | |
| - | + | ||
| - | * {{ :laboratoare:laborator08:aipi2014-lab08-eclipseee.zip }} (proiect EclipseEE ) | + | |
| - | + | ||
| - | * {{ :laboratoare:laborator08:aipi2014-lab08-netbeans.zip }} (proiect NetBeans ) | + | |
| - | + | ||
| - | * {{ :laboratoare:laborator08:aipi2014-lab08-noide.zip }} (proiect NoIDE - fără mediu integrat de dezvoltare) | + | |
| - | + | ||
| - | **1.** [**0 puncte**] Să se instaleze baza de date prin rularea script-ului ''Laborator08.sql''. | + | |
| - | + | ||
| - | <note>Conţinutul script-ului'' Laborator08.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''. | + | Va fi apelat utilitarul ''hbase'' (cu parametrul ''shell''), indicându-se căile absolute către scripturile care trebuie rulate. |
| <code bash> | <code bash> | ||
| - | aipi2014@ubuntu:~/apache-tomcat-8.0.15$ cd bin | + | student@aipi2015:~/scripts$ hbase shell Laborator08_DDLc.rb |
| - | aipi2014@ubuntu:~/apache-tomcat-8.0.15/bin$ sudo chmod +x *.sh | + | student@aipi2015:~/scripts$ hbase shell Laborator08_DML.rb |
| </code> | </code> | ||
| - | **3.** [**0 puncte**] Să se completeze în clasa ''Constants'' din pachetul ''ro.pub.cs.aipi.lab08.general'' valorile pentru utilizatorul şi parola pentru accesarea bazei de date. | + | <note tip>De asemenea, există un script ''Laborator08_DDLd.rb'' pentru dezinstalarea bazei de date, în cazul în care o astfel de operație este necesară.</note> |
| - | **4.** [**0 puncte**] | + | Structura bazei de date conține următoarele tabele, familii de coloane și atribute: |
| - | ==== No IDE ==== | + | <html> |
| + | <center> | ||
| + | </html> | ||
| - | Să se specifice variabilele de mediu ''JAVA_HOME'' şi ''JAVA_JRE'' în script-urile ''setClasspath[.bat|.sh]'' (din directorul ''bin'' al serverului Apache Tomcat). | + | ^ Tabela ^ Familia de Coloane ^ Atributul ^ |
| + | | country | identification | name, code | | ||
| + | | ::: | detail | description | | ||
| + | | publishing_house | identification | name, registered_number | | ||
| + | | ::: | detail | description | | ||
| + | | ::: | address | postal_address, zip_code, country_id | | ||
| + | | ::: | miscellanea | internet_address | | ||
| + | | collection | identification | name, description | | ||
| + | | ::: | referrence | publishing_house_id | | ||
| + | | book | identification | title, subtitle | | ||
| + | | ::: | detail | description | | ||
| + | | ::: | version | edition, printing_year | | ||
| + | | ::: | group | collection_id | | ||
| + | | format | identification | value | | ||
| + | | ::: | detail | description | | ||
| + | | language | identification | name, code | | ||
| + | | ::: | detail | description | | ||
| + | | book_presentation | identification | isbn | | ||
| + | | ::: | referrence | book_id, format_id, language_id | | ||
| + | | ::: | inventory | stockpile, price | | ||
| + | | writer | appellation | first_name, last_name | | ||
| + | | ::: | biography | content | | ||
| + | | author | referrence | book_id, writer_id | | ||
| + | | category | identification | name | | ||
| + | | ::: | detail | description | | ||
| + | | category_content | referrence | book_id, category_id | | ||
| + | | supply_order_hdeader | identification | identification_number, issue_date | | ||
| + | | ::: | situation | state | | ||
| + | | ::: | producer | publishing_house_id | | ||
| + | | supply_order_line | referrence | supply_order_header_id | | ||
| + | | ::: | content | book_presentation_id, quantity | | ||
| + | | user | appellation | first_name, last_name | | ||
| + | | ::: | identification | personal_identifier | | ||
| + | | ::: | contact | address, phone_number, email | | ||
| + | | ::: | category | type | | ||
| + | | ::: | authentication | username, password | | ||
| + | | invoice_header | identification | identification_number, issue_date | | ||
| + | | ::: | situation | state | | ||
| + | | ::: | consumer | user_id | | ||
| + | | invoice_line | referrence | invoice_header_id | | ||
| + | | ::: | content | book_presentation_id, quantity | | ||
| + | | statistics | expense | value | | ||
| - | Să se "publice" aplicaţia BookStore în contextul serverului Apache Tomcat prin rularea script-ului ''deploy[.bat|.sh]'' din directorul ''scripts''. | + | <html> |
| + | </center> | ||
| + | </html> | ||
| - | <note>De remarcat faptul că aplicaţia ''BookStore'' se găseşte în directorul ''webapps'' al serverului Apache Tomcat.</note> | + | **5.** Se doreşte afişarea unei liste conţinând toţi utilizatorii (identificaţi prin nume şi prenume) precum şi sumele pe care aceştia le-au cheltuit prin achiziţia de cărţi din librăria virtuală. |
| - | <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> | + | În cazul unui sistem de gestiune pentru baze de date relaţionale, o astfel de problemă are o soluţie foarte simplă ce constă într-o operaţie de joncţiune între tabelele ''book_presentation'', ''user'', ''invoice_header'' şi ''invoice_line''. Pentru un volum foarte mare de informaţii, o astfel de operaţie poate dura o perioadă de timp inacceptabilă, motiv pentru care se doreşte ca procesarea să se realizeze în paralel, pe mai multe maşini, fiecare gestionând o parte dintre înregistrările tabelelor respective. |
| - | ==== Eclipse ==== | + | Un sistem MapReduce poate împărţi datele de intrare (tabela ''invoice_line'' pentru situaţia de faţă) în mai multe regiuni în mod automat, fiecare dintre acestea fiind prelucrate de un proces separat. Astfel, pentru fiecare detaliu din factură, reprezentând un produs al facturii se determină preţul total, obţinut prin înmulţirea preţului formatului de prezentare a cărţii (obţinut printr-o interogare a tabelei ''book_presentation'') cu cantitatea precizată. Pentru o factură dată prin identificatorul său, se asociază preţul total al fiecărui produs pe care îl conţine. Ulterior, se creează o listă corespunzătoare fiecărei facturi conţinând toate preţurile totale ale produselor aferente. Acestea vor fi însumate, obţinându-se valoarea facturii. |
| - | Să se creeze o [[:laboratoare:laborator07#eclipse|referinţă către serverul Apache Tomcat]] care să fie adăugată la proiectul BookStore. | + | <html> |
| + | <center> | ||
| + | </html> | ||
| - | Să se deschidă proiectul folosind opţiunea //File// → //Import// → //General// → //Existing Project into Workspace..//. | + | ^ MAP ^ → ^ REDUCE ^ |
| + | | (factura1, preţ_produs1) | → | (factura1, (preţ_produs1, preţ_produs2, ..., preţ_produsm1))\\ =\\ (factura1, preţ_produs1 + preţ_produs2 + ... + preţ_produsm1) | | ||
| + | | (factura1, preţ_produs2) | ::: | ::: | | ||
| + | | ... | ::: | ::: | | ||
| + | | (factura1,preţ_produsm1) | ::: | ::: | | ||
| + | | (factura2, preţ_produs1) | ::: | (factura2, (preţ_produs1, preţ_produs2, ..., preţ_produsm1))\\ =\\ (factura2, preţ_produs1 + preţ_produs2 + ... + preţ_produsm2) | | ||
| + | | (factura2, preţ_produs2) | ::: | ::: | | ||
| + | | ... | ::: | ::: | | ||
| + | | (factura2,preţ_produsm2) | ::: | ::: | | ||
| + | | ... | ::: | ... | | ||
| + | | (facturan, preţ_produs1) | ::: | (facturan, (preţ_produs1, preţ_produs2, ..., preţ_produsmn))\\ =\\ (facturan, preţ_produs1 + preţ_produs2 + ... + preţ_produsmn) | | ||
| + | | (facturan, preţ_produs2) | ::: | ::: | | ||
| + | | ... | ::: | ::: | | ||
| + | | (facturan,preţ_produsmn) | ::: | ::: | | ||
| - | 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//. | + | <html> |
| + | </center> | ||
| + | </html> | ||
| - | {{ :laboratoare:laborator07:eclipse05.png?nolink&600 }} | + | Cum fiecare factură are asociat un identificator al utilizatorului care a achitat-o, acesta va conduce la obţinerea, prin interogarea bazei de date user, a numelui şi prenumelui clientului respectiv. |
| - | {{ :laboratoare:laborator07:eclipse06.png?nolink&600 }} | + | Pentru a calcula totalurile pentru fiecare utilizator (un utilizator poate avea mai multe facturi asociate), din datele generate până acum, se vor realiza grupări în funcţie de utilizatori, astfel încât pentru fiecare dintre aceştia vor fi asociate una sau mai multe sume de bani corespunzătoare facturilor. Printr-un mecanism similar, acestea vor fi însumate în vederea determinării cheltuielilor totale, pentru fiecare utilizator în parte. |
| - | {{ :laboratoare:laborator07:eclipse07.png?nolink&600 }} | + | <html> |
| + | <center> | ||
| + | </html> | ||
| - | ==== NetBeans ==== | + | ^ MAP ^ → ^ REDUCE ^ |
| + | | (utilizator, preţ_factura1) | → | (utilizator, (preţ_factura1, preţ_factura2, ..., preţ_facturan))\\ =\\ (utilizator, preţ_factura1 + preţ_factura2 + ... + preţ_facturan) | | ||
| + | | (utilizator, preţ_factura2) | ::: | ::: | | ||
| + | | ... | ::: | ::: | | ||
| + | | (utilizator, preţ_facturan) | ::: | ::: | | ||
| - | Să se creeze o [[:laboratoare:laborator07#netbeans|referinţă către serverul Apache Tomcat]] care să fie adăugată la proiectul BookStore. | + | <html> |
| + | </center> | ||
| + | </html> | ||
| - | Se accesează opţiunea //Deploy// din meniul contextual al proiectului (accesat cu right-click). | + | Se observă faptul că structura coloanelor marcate este echivalentă, astfel încât aplicaţia va consta din 2 sarcini, din care una va avea atât partiţie de tip map cât şi partiţie de tip reduce, iar una va avea numai partiţie de tip reduce. De asemenea, se observă faptul că datele de ieşire ale unei sarcini reprezintă datele de intrare pentru cealaltă sarcină. |
| - | {{ :laboratoare:laborator07:netbeans05.png?nolink }} | + | În aplicaţia ''BookStore'', cele două sarcini se numesc ''invoiceValue'' şi ''userExpenses'', fiind definite (prin partiţiile map şi reduce corespunzătoare) în clasa cu acelaşi nume. |
| - | <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> | + | **a)** în clasa ''Map'' a sarcinii ''invoiceValue'' (clasa ''InvoiceValueMapper'' din pachetul ''ro.pub.cs.aipi.lab08.mapper''), să se determine preţul formatului de prezentare a cărţii analizate în mod curent (care se găseşte în detaliul de factură repartizat procesului curent); |
| - | ===== ===== | + | <spoiler|Indicații de rezolvare> |
| - | + | Se va crea o instanţă a clasei ''Table'', pe baza unui obiect de tip ''Connection'' (obținut prin intermediul unei fabrici de conexiuni, ''ConnectionFactory''), pentru care se apelează metoda ''createConnection()'', aceasta primind ca argument denumirea tabelei respective (''TableName.valueOf(...)''). Ulterior, se creează un obiect de tip ''Get'' în care se va specifica atributul ce se doreşte obţinut (prin metoda ''addColumn()'', indicându-se atât familia coloanei cât şi denumirea propriu-zisă a coloanei), din aplicarea acestuia generându-se un obiect ''Result'' pentru care se apelează metoda ''getValue()'' în scopul de a se obţine valoarea dorită. | |
| - | **5.** [**0 puncte**] Să se testeze aplicaţia prin accesarea adresei http://localhost:8080/BookStore/. | + | |
| - | <note tip> | + | <note important>Nu uitați să eliberați resursele asociate atât tabelei HBase pe care se realizează interogarea cât și a conexiunii pe baza căreia aceasta a fost obținută, prin apelarea metodei ''close()''.</note> |
| - | Se poate consulta script-ul ''Laborator08.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). | + | </spoiler> |
| - | **7.** [**10 puncte**] În pagina ''client.jsp'', să se construiască clauza ''WHERE'' a interogării corespunzătoare tabelei ''book'', astfel încât conţinutul tabelei să fie filtrat după valoarea selectată din lista de colecţii / domenii. | + | **b)** în clasa ''Reduce'' a sarcinii ''invoiceValue'' (clasa ''InvoiceValueReducer'' din pachetul ''ro.pub.cs.aipi.lab08.reducer''), să se calculeze preţul total pentru o factură, prin agregarea tuturor sumelor parţiale specificate în detaliile de factură; |
| - | <note tip>Filtrarea se va realiza numai în cazul valorile selectate sunt diferite de şirul de caractere 'all' (= ''Constants.ALL''). În cazul în care vor fi specificate ambele valori, condiţia de filtrare va presupune îndeplinirea ambelor criterii.</note> | + | **c)** în clasa ''Reduce'' a sarcinii ''invoiceValue'' (clasa ''InvoiceValueReducer'' din pachetul ''ro.pub.cs.aipi.lab08.reducer''), să se obţină, prin interogarea tabelei ''invoice_header'', identificatorul utilizatorului căruia i-a fost emisă factura analizată în mod curent; |
| - | <note tip>Pentru fiecare colecţie / domeniu există mai mulţi identificatori în tabelele aferente (colecţii / domenii) care au denumirea respectivă, astfel încât interogarea va avea forma: <code mysql> | + | **d)** în clasa ''Reduce'' a sarcinii ''invoiceValue'' (clasa ''InvoiceValueReducer'' din pachetul din pachetul ''ro.pub.cs.aipi.lab08.reducer''), să se obţină, prin interogarea tabelei ''user'', numele şi prenumele clientului având identificatorul determinat anterior; |
| - | SELECT ... FROM book | + | |
| - | WHERE | + | |
| - | series_id IN (SELECT id FROM series WHERE name='... ') | + | |
| - | AND | + | |
| - | genre_id IN (SELECT id FROM genre WHERE name= '... '); | + | |
| - | </code></note> | + | |
| - | <note tip>În specificaţia EL 2.2 concatenarea a două şiruri de caractere se face prin alăturarea lor (operatorul + pentru şiruri de caractere este definit în EL 3.0): <code java> | + | **e)** în clasa ''BookStore'' din pachetul ''ro.pub.cs.aipi.lab08.main'', pentru sarcina ''userExpenses'', să se indice valorile claselor corespunzătoare cheii şi valorii datelor de ieşire; |
| - | <c:set scope= "request" var= "myvar" value="${var1} some text ${var2}" /> | + | |
| - | </code></note> | + | |
| - | **8.** [**35 puncte**] În pachetul ''ro.pub.cs.aipi.lab08.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. | + | <spoiler|Indicații de Rezolvare> |
| + | Întrucât datele de ieşire sunt reprezentate de tuplul (nume&prenume, valoare), tipurile de date corespunzătoare vor fi ''Text'', respectiv ''DoubleWritable''. | ||
| + | </spoiler> | ||
| - | Î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. | + | **f)** în clasa ''Reduce'' a sarcinii ''userExpenses'' (clasa ''UserExpensesFileReducer'' din pachetul ''ro.pub.cs.aipi.lab08.reducer''), să determine valoarea totală a facturilor achitate de client şi să se scrie această valoare în context (folosind metoda ''write()'', cheia de rând fiind reprezentată de numele şi prenumele utilizatorului); |
| - | 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. | + | De remarcat faptul că pentru citirea datelor de intrare în cazul sarcinii ''userExpenses'', a fost folosită o clasă specială, definită de utilizator (''CustomFileInputFormat''), în care datele au forma (cheie, valoare), iar separatorul dintre aceste entităţi este TAB-ul. Întrucât cheia este reprezentată de numele şi prenumele utilizatorilor, iar valoarea de suma unei facturi, tipurile de date procesate în clasa ''CustomFileInputFormatRecordReader'' vor fi ''Text'', respectiv ''DoubleWritable''. O astfel de soluţie a fost adoptată datorită faptului că nu au fost definite tipuri de fişiere de intrare care să aibă această structură. |
| - | <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)''. | + | **6.** Să se rezolve problema anterioară, stocând rezultatele nu într-un fişier ci în tabela ''statistics'' a bazei de date HBase, care are o singură familie de coloane, ''expense'', în care o posibilă coloană ar putea fi valoare. |
| - | 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. | + | **a)** În clasa ''BookStore'' din pachetul ''ro.pub.cs.aipi.lab08.main'', se va apela metoda statică ''initTableReducerJob()'' a clasei ''TableMapReduceUtil'' ce primeşte ca parametrii denumirea tabelei unde se doresc a fie redirectate datele de ieşire, numele clasei care implementează sarcina de tip ''Reduce'' şi numele obiectului de tip ''Job''. De asemenea, va trebui indicat şi numărul sarcinilor de tip ''Reduce''. |
| - | 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> | + | **b)** În clasa ''UserExpensesTableReducer'' din pachetul ''ro.pub.cs.aipi.lab08.reducer'', ulterior determinării sumei totale achitate de client, să se creeze un obiect de tip ''Put'' având drept cheie numele şi prenumele utilizatorului, adăugându-se (prin metoda ''addColumn()''), ca valoare a celulei identificată de coordonatele ''expense:value'', suma tuturor facturilor, corespunzând produselor achiziţionate. |
| - | **9.** [**15 puncte**] În pagina ''client.jsp'', să se afişeze conţinutul coşului de cumpărături. | + | **7**. Să se verifice rezultatele obținute. |
| - | Î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. | + | <spoiler|Indicații de Rezolvare> |
| + | **a)** în situația în care rezultatele au fost stocate în sistemul de fișiere HDFS, se poate folosi plugin-ul pentru mediul integrat de execuție Eclipse, care conferă un navigator pentru acest sistem de fișiere;\\ | ||
| + | **b)** în situația în care rezultatele au fost stocate în tabela ''statistics'' din cadrul sistemului de gestiune pentru baze de date distribuite ''HBase'', se poate verifica conținutul acesteia: <code> | ||
| + | student@aipi2015:~$ hbase shell | ||
| + | hbase> scan 'statistics' | ||
| + | </code> | ||
| + | </spoiler> | ||
| - | {{ :laboratoare:laborator07:exercitiile9_10.png?nolink }} | + | **8.** Înainte de a opri maşina virtuală, să se oprească instanţa sistemului de gestiune pentru baze de date distribuite HBase precum şi a sistemului de gestiune a resurselor YARN, respectiv al sistemului de fişiere distribuite HDFS. |
| - | <note tip>În cazul în care rezultatul unei interogări se află într-un obiect ''result'', elementele sale pot fi obţinute prin indexare, începând cu 0: ''result.rows[0]'', ''result.rows[1]'', ..., ''result.rows[n-1]''. Acestea sunt nişte tablouri care conţin toate atributele interogării, acestea putând fi accesate tot prin indexare, prin numele lor: ''result.rows[k]['attribute1']'', ''result.rows[k]['attribute2']'', ..., ''result.rows[k]['attributem']''.</note> | + | <code> |
| - | + | student@aipi2015:~$ stop-hbase.sh | |
| - | **10.** [**10 puncte**] În pagina ''client.jsp'' să se 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). | + | student@aipi2015:~$ stop-yarn.sh |
| - | + | student@aipi2015:~$ stop-dfs.sh | |
| - | <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.lab08.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''. | + | |
| - | + | ||
| - | În cazul finalizării unei comenzi, trebuie realizate următoarele operaţii: | + | |
| - | * 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 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 ''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 ''invoice_detail''; | + | |
| - | * se goleşte coşul de cumpărături. | + | |
| - | </note> | + | |
| - | + | ||
| - | <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> | </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 (''login.jsp''). | ||
| - | |||
| - | {{ :laboratoare:laborator07:exercitiul12.png?nolink }} | ||
| - | |||
| - | <note tip>Se va lucra în clasa ''ClientServlet'', plasarea butonului realizându-se în pagina ''client.jsp''.</note> | ||
| - | |||
| - | <note tip>Puteți folosi ca model implementarea din cadrul paginii de administrator (clasa ''AdministratorServlet'' / pagina ''administrator.jsp'').</note> | ||
| - | |||
| - | <note important>Înainte de transferul contextului către pagina 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> | ||
| - | |||
| - | <note important>După apelul metodei ''forward()'' pentru obiectul ''requestDispatcher'', este recomandat să se apeleze instrucțiunea ''return'' pentru că apelul ulterior al altor metode poate genera o excepţie de tipul ''IllegalStateException''.</note> | ||
| ===== Resurse ====== | ===== Resurse ====== | ||
| - | [[https://docs.oracle.com/javaee/7/tutorial/doc/javaeetutorial7.pdf|Eric Jendrock, Ricardo Cervera- | + | [[http://hadoop.apache.org/docs/current/|Hadoop Documentation]]\\ |
| - | Navarro, Ian Evans, Kim Haase, William Markito - The Java EE 7 Tutorial]] - capitolul 9\\ | + | [[http://it-ebooks.info/book/635/|Tom WHITE, Hadoop – The Definitive Guide, 3rd Edition, O’Reilly, 2012]]\\ |
| - | [[http://tomcat.apache.org/|Apache Tomcat]]\\ | + | [[http://www.coreservlets.com/hadoop-tutorial|Hadoop Tutorial: Developing Big-Data Applications with Apache Hadoop]]\\ |
| - | [[http://www.tutorialspoint.com/jsp/|Basic JSP Tutorial]]\\ | + | [[http://hbase.apache.org/book/book.html|The Apache HBaseTM Reference Guide]]\\ |
| - | [[http://www.java-samples.com/jsp/|Knowledge Base on JSP]]\\ | + | [[http://books.google.ro/books?id=nUhiQxUXVpMC&printsec=frontcover&source=gbs_ge_summary_r&cad=0#v=onepage&q&f=false|Lars GEORGE, HBase: The Definitive Guide, O’Reilly, 2011]] |
| - | [[http://www.javatpoint.com/jsp-tutorial|JSP Tutorial]]\\ | + | |
| - | [[http://www.jsptut.com/|JSP Tutorial]]\\ | + | |
| - | [[https://www.hscripts.com/tutorials/jsp/|JavaServer Pages Tutorial]]\\ | + | |
| - | [[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]]\\ | + | |
| - | [[http://www.vogella.com/tutorials/EclipseWTP/article.html|Java web development with Eclipse WTP - Tutorial]]\\ | + | |
| - | [[http://books.google.com/books?isbn=1430204656|Giulio Zambon with Michael Sekler – Beginning JSPTM, JSFTM and Tomcat Web Development: From Novice to Professional, Apress, 2007]]\\ | + | |
| - | Irina ATHANASIU, //Java ca limbaj pentru programare distribuita//, Editura Matrix Rom, București, 2002 | + | |
| ===== Soluții ===== | ===== Soluții ===== | ||
| - | [[https://github.com/aipi2014/Laborator08/tree/master/EclipseEE/|Proiect EclipseEE]]\\ | + | [[https://github.com/aipi2015/Laborator08/tree/master/solutions|Proiect Eclipse]] |
| - | [[https://github.com/aipi2014/Laborator08/tree/master/NetBeans/|Proiect NetBeans]] | + | |
