Aplicatie Web Pentru Structurarea Informatiilor Intr O Platforma Educationala
Cuprins
Bbibliografie 45
În ultimul deceniu, conceptul de „e-learning” (învățare prin mijloace electronice, de obicei pe internet) a transformat radical modul de asimilare a informațiilor, modul de trai și munca fiecăruia, evoluția acestui domeniu fiind strâns legată de evoluția tehnologiei. Flexibilitatea acestei metode de învățare a devenit o necesitate pentru omul modern, care, aflat în continuă evoluție și dezvoltare personală, dorește să poată decide când, unde, cum și ce să învețe.
Lucrarea de față prezintă modul de funcționare și pașii necesari implementării unei platforme educaționale care vine în întâmpinarea acestor nevoi, prin intermediul căreia comunitatea de utilizatori are posibilitatea de a împărtăși cu ceilalți informații din diferite domenii. Informațiile vor fi structurate în categorii, subcategorii, cursuri, iar în cadrul cursurilor pot exista mai multe lecții. Pentru a asigura corectitudinea și integritatea datelor, va exista rolul de administrator, ce va avea drepturi de editare a conținutului, ștergere sau adăugare de noi informații.
Interfața grafică este facilă atât utilizatorilor ce contribuie la îmbogățirea platformei cu noi cursuri, cât și vizitatorilor, care beneficiază de informațiile puse la dispoziție sub formă de text, fișiere pdf sau lecții video.
1. Introducere
Evoluția experienței învățatului a fost întotdeauna strâns legată de evoluția tehnologiei. După cum afirmă specialiștii, învățământul la distanță a trecut, de-a lungul timpului, prin trei etape importante: studiul prin corespondență, învățământul multimedia și în final, cel online, pe calculator, foarte răspândit astăzi.
Conceptul de e-learning a început să se dezvolte cu adevărat în urmă cu un deceniu, când protocolul SCORM (Sharable Content Object Reference Model) domina piața. În cei zece ani ce au urmat s-au produs multe schimbări, în special în ceea ce privește tehnologia, noi tendințe apărând și dispărând. Totuși, ceea ce a rămas constant în acest progres este modul în care tehnologia a impactat metodologia furnizării conținutului. Astfel, oamenii au început să gândească mai creativ modul în care se poate pune la dispoziție material de învățare unui grup de indivizi.
În ziua de astăzi, acest proces de e-learning poate fi întâlnit în majoritatea universităților, dar și în învățământul inferior. Totodată, pe lângă răspândirea în mediul academic, conceptul de învățare online a a vut succes și în mediile extrașcolare. Pentru omul modern, aflat în permanentă mișcare și întotdeauna în lipsă de timp, este mult mai comod să capete informații din mediul online, unde poate avea acces în orice moment, în schimbul urmării unor cursuri fizice, care necesită timp, și de multe ori resurse financiare.
1.1 Obiective
Prin acest proiect am urmărit dezvoltarea unei aplicații web care să vină în întâmpinarea nevoii de învățare în mediul online și care să reunească informații dintr-o varietate de domenii, având cursuri ce prezintă de la cele mai simple noțiuni până la subiecte din domenii de activitate de nivel academic, într-o interfață cât mai ușor de folosit.
Am dorit să las libertate utilizatorilor să construiască și să îmbogățească această platformă cu informații din categorii alese de ei, fără a restricționa în vreun fel domeniile în care se pot adăuga cursuri, tocmai pentru a oferi o gama largă de opțiuni celor ce doresc să învețe, să se dezvolte profesional și să-și îmbogățească cultura generală.
Totuși, pentru ca aplicația să-și îndeplinească scopul, acela de informare, este foarte important să existe un conținut corect, adecvat tipului de software educațional din care face parte. Pentru aceasta, vor exista utilizatori cu rol de administratori, care vor avea dreptul să editeze sau să elimine conținutul necorespunzător, și de asemenea să șteargă conturile utilizatorilor ce au adăugat informațiile respective.
Am dorit ca trăsătura principală a acestei platforme să fie varietatea, și tocmai de accea am oferit utilizatorilor posibilitatea să adauge cursuri în trei formate diferite, și anume cursuri cu conținut text, fișiere pdf și tutoriale video. Pe de altă parte, existând atât de multe cursuri din domenii atât de variate, poate apărea riscul încărcării excesive a aplicației, navigarea devenind dificilă. Am încercat să evit acest risc prin structurare informațiilor intr-o ierarhie de categorii, subcategorii, cursuri și lecții, astfel încât produsul final să fie cât mai intuitiv utilizatorului.
1.2 Contextul actual
În prezent, sistemul educativ online poate fi împărțit în 3 categorii: cursuri în care nu există nici un fel de interacțiune fizică cu profesorul sau cu cel ce oferă cursul utilizatorilor, cursuri în care mai mult de jumătate din conținut este încărcat în mediul online (de obicei în această categorie intră acele cursuri predate în modul tradițional, dar care se folosesc ca suport și de internet) și, ultima categorie, cursuri al căror conținut este pus la dispoziție în mediul virtual în proporție mică, bazându-se foarte mult pe conactul fizic între profesor și elev.
Așadar modul de învățare s-a schimbat, trecând de la convenționalele cursuri ținute de profesori în clasă, la cursurile online, pe care le poate accesa oricine are un computer sau un dispozitiv mobil cu acces la internet. Numărul tipurilor de tehnologii ce oferă posibilitatea învățării online s-a dublat în ultimii cinci ani, câteva din cele mai populare sisteme de management al învățării (Learning Management Systems) de pe internet fiind Moodle, Edmodo și Blackboard, cu un numar de utilizatori de ordinul milioanelor.
Un studiu făcut asupra persoanelor ce urmează astfel de cursuri online [2] arată că principala lor motivație este creșterea profesională, avansarea în carieră, iar majoritatea lucrează în domeniul afacerilor. Ca medie de vârstă, cei ce frecventează site-uri din acest domeniu se încadrează între 18 și 29 de ani, 60% fiind angajați full-time, ceea ce nu le permite urmarea unor cursuri tradiționale.
Astfel, popularitatea învățământului online a crescut din nevoia oamenilor de a se dezvolta intelectual, de a dobândi informații cât mai rapid și mai comod posibil, fără a fi nevoie să piardă timp cu urmarea unor cursuri tradiționale, unde se respectă un program fix. De asemenea, multe din cursurile online sunt gratuite, ceea ce reprezintă încă un avantaj.
1.3 Motivație
Am ales să implementez o aplicație cu scop educativ, ce răspunde nevoilor utilizatorilor, punând la dispoziția acestora o varietate mare de domenii de învățare, având cursuri ce vor fi accesate gratuit și vor putea fi adăugate de utilizatorii ce doresc să contribuie la îmbogățirea acesteia.
În contextul actual, în care tot mai mulți oameni doresc să capete informații rapid, să se dezvolte pe plan profesional, să acumuleze noi cunoștințe, tehnicile de învățare precum prezentările, tutorialele video sau informațiile însoțite de imagini, așa cum sunt implementate în aplicația mea, fac procesul de asimilare a informațiilor mult mai ușor.
De asemenea, beneficiul învățări de acasă, din confortul propriului mediu de lucru sau flexibilitatea pe care o oferă existența unei platforme educaționale ce poate fi accesată la orice oră, din orice loc, atât timp cât ai la dispoziție un calculator sau un dispozitiv mobil, conectat la internet, toate acestea sunt avantaje incontestabile ce pun pe primul plan opțiunea urmării unor cursuri online, în detrimentul cursurilor tradiționale.
2. Soluția oferită
Aplicația implementată de mine reprezintă o platformă educațională ce oferă utilizatorilor posibilitatea accesării de informații, structurate în diferite domenii, pentru a face mai rapidă căutarea cursurilor. Astfel, informațiile sunt impărțite pe categorii, subcategorii, cursuri și lecții, iar meniul din care din care acestea se pot accesa este în permanență vizibil utilizatorului, în orice pagină s-ar afla.
Îmbogățirea conținutului va fi posibilă prin contribuția utilizatorilor, care vor putea încărca noi cursuri, creându-și propria structură de categorii și subcategorii, sau folosind deja ierarhia existentă, iar relevanța acestora va fi stabilită prin vot, de către ceilalți utilizatori. Nu există noțiunea de student și profesor, ca în alte sisteme de învățare online similare, oricine deține cursuri, tutoriale video sau prezentari și dorește să le împărtășască cu ceilalți putând-o face prin această platformă.
Aplicația a fost scrisă în limbajul de programare Java, peste care am integrat framework-ul Spring prin care am beneficiat de ușurinta în dezvoltare. Baza de date folosită este MYSQL, și în ea se țin datele referitoare la structura conținutului aplicației, detaliile despre cursuri precum și informații despre conturile utilizatorilor. Conținutul propriu zis al cursurilor în format pdf sau video nu va fi stocat în baza de date, pentru a evita încărcarea inutilă a acesteia, ci va fi salvat pe server. Pentru interfață am folosit tehnologia JSF (Java Server Faces) care oferă posibilitatea construirii facile a componentelor afișate, precum tabele, meniuri, template-uri de ecrane.
2.1 Schema bloc a aplicației
Figura de mai jos prezintă diagrama bloc a plicației.
Fig. 1. Schema bloc a aplicației
După cum se poate observa, aplicația poate fi împărțită în doua mari nivele de implementare, care comunică între ele, și anume frontend-ul, ce conține partea de interfață grafică, paginile JSF, care sunt transformate în pagini HTML (Hyper Text Markup Language) și apoi afișate utilizatorului prin intermediul unui browser, și backend-ul.
Pginile JSF comunica cu nivelul de backend (codul Java al aplicației), prin intermediul unor clase numite Controllere, care administrează modul în care datele sunt transferate în interfață și evenimentele inițializate de utilizator în aplicației (trimitere de formulare, click-uri prin pagini, etc). Controllerele comunică la rândul lor cu serviciile. Acestea sunt clasele în care este implementată cea mai importantă parte a aplicației, și anume logica de business. Aici se fac prelucrări asupra datelor, datele sunt filtrate în funcție de selecțiile făcute de utilizator și apoi trimise controllerlor. Ultima componentă este alcătuită din clasele care comunică în mod direct cu baza de date. Obiectele DAO (Data Access Objects), dupa cum le spune și numele, furnizează accesul la baza de date și pun la dispoziție metode prin care aceasta este interogată, iar datele obținute sunt transformate în obiecte Java.
2.2 Descrierea funcțională
Scopul principal al aplicației este oferirea de informații din diverse domenii, utilizatorilor ce o accesează. Așadar, cea mai importantă funcționalitate, cea care îndeplinește scopul principal al aplicației, este reprezentată de posibilitatea vizualizării acestor informații de către utilizatorii interesați, într-o interfață cu o navigare intuitiva și facilă. Totodată, utilizatorii autentificați vor avea posibilitatea să voteze cursurile, în funcție de relevanța fiecăruia.
O altă funcționalitate foarte importantă, fără de care nu ar exista conținut care să poată fi vizualizat, este adăugarea de cursuri de către utilizatori, aceasta făcând posibilă popularea aplicației cu date utile celor ce o accesează. De asemena, un rol important îl are și funcționalitatea de editare și ștergere a cursurilor, care permite administratorilor să asigure integritatea și corectitudinea datelor furnizate de utilizatori.
Aceste fucționalități nu sunt disponibilie tuturor utilizatorilor, ci sunt restricționate în funcție de rolul fiecăruia. Așadar, există trei tipuri de actori în aplicație (trei roluri pe care le pot avea utilizatorii): vizitator, utilizator autentificat și administrator, fiecare putând realiza anumite seturi de operații.
Fig. 2. Cazuri de utilizare pentru un utilizator neautentificat
Figura de mai sus ilustrează cazurile de utilizare ale aplicației de către un vizitator (utilizator neautentificat). Acesta are dreptul să vizualizeze cursuri, putând naviga printre ele folosind ierarhia de categorii din meniul lateral stânga. Dacă nu are deja un un cont în aplicație, își va putea crea unul, pe baza lui urmând să se autentifice, iar în cazul în care s-a înregistrat deja, se poate autentifica direct, introducând un nume de utilizator și o parolă. După autentificare, utilizatorul va primi acces la mai multe funcționalități ale aplicației, ca utilizator autentificat.
Mai jos sunt prezentate cazurile de utilizare pentru un utilizator ce s-a autentificat cu succes în aplicație. Pe lângă funcționalitățile disponibile vizitatorului, acesta are posibilitatea de a adăuga un curs, selectând în prealabil categoria și subcategoria din care face parte cursul respectiv, având și opțiunea de a alege formatul în care va uploada cursul: format text, pdf sau video. De asemenea, acest tip de actor are dreptul să acorde voturi cursurilor pe care le vizualizează, în acest mod oferind feedback utilizatorului ce a adăugat cursul, dar și celorlalți utilizatori interesați de informațiile din acel curs.
Pe lângă funcționalitățile legate de conținut, utilizatorului îi sunt disponibile și funcționalități referitoare la contul său și autentificarea în aplicție. Astfel, acesta se poate deloga sau loga, și are posibilitatea de recuperare a parolei, prin furnizarea unei adrese de email valide (cea cu care s-a înregistrat în aplicație).
Fig. 3. Cazuri de utilizare pentru un utilizator autentificat
Ultimul tip de actor, și anume administratorul, are drepturi depline asupra tuturor funcționalităților existente în aplicație. După cum se poate observa în figura 4, pe lângă posibilitatea executării tuturor operațiilor specifice unui utilizator autentificat, acesta are și drepturi de editare și ștergere de conținut, dar și de conturi ale altor utilizatori.
Fig. 4. Cazuri de utilizare pentru administrator
2.3 Arhitectura soluției
Pentru structurarea proiectului am folosit MVC (Model View Controller), unul din cele mai populare design-uri arhitecturale în programarea web. Ideea după care se ghidează MVC este una simplă: trebuie să existe o separare în aplicție între următoarele funcționalități:
• Manipularea datelor și logica de business
• Prezentarea datelor către utilizator, într-un format suportat
• Primirea de cereri de la utilizator și apelarea resurselor potrivite
Aplicația este împărțită în aceste trei componente, Model, View și Controller, fiecare îndeplinind anumite funcționalități:
Controller-ul administrează cererile de la utilizator (primite ca cereri HTTP GET sau POST atunci când se apasă butoane din interfața grafică și se execută acțiuni). Scopul său este de a apela și coordona resursele sau obiectele necesare executării acțiunii utilizatorului. Controller-ul apelează apoi modelul corespunzător pentru îndeplinirea task-ului, și selectează view-ul potrivit pentru afișare.
Modelul reprezintă datele și regulile ce guvernează aceste date. Acesta conferă controller-ului o reprezentare a datelor cerute de utilizator, modelul datelor ramânând același indiferent de modul în care se face prezentarea lor utilizatorului (deci se poate folosi orice view disponibil).
View-ul pune la dispoziție diferite moduri de reprezentare a datelor primite de la model, și pot exista mai multe view-uri din care controllerul poate alege.
Avantajul principal al folosirii MVC este separarea clară între nivelul de prezentare (interfața cu utilizatorul) și datele efective. Astfel, daca există mai multe moduri de reprezentare a datelor (mai multe view-uri), se va înlocui doar nivelul view, codul pentru controller si model putând fi reutilizat. Pe lângă aceasta, MVC reduce complexitatea dezvoltării de aplicații mari, codul fiind mult mai bine structurat și deci mai ușor de întreținut și testat.
Maparea acestui design în proiectul meu, care este o aplicație web ce folosește JSF (Java Server Faces) este descrisă în cele ce urmează:
Model ul este definit de bean-urile administrate de JSF, care interacționează cu componente de logică de business ce pot fi reutilizate, mai exact cu baza de date MySQL.
View-ul prezintă datele din model folosind pagini JSP (Java Server Pages). Aceste pagini sunt construite prin utilizarea elementelor de interfață grafică predefinite și prin legarea lor de model.
Controller-ul procesează evenimentele generate de utilizator și actualizează modelul și view-ul.
Figura 5 ilustrează modul în care se desfășoară procesarea unei cereri, respectând design-ul arhitectural MVC. Ca orice framework pentru aplicații web, JSF folosește modelul MVC pentru a decupla datele de interfața cu utilizatorul. Pentru a trata cereri de la utilizator în mod centralizat, controller-ul face modificări in model și direcționează user-ul către view-ul corespunzător.
Când un utilizator face o cerere pentru o pagină JSF, cererea ajunge la FacesServlet (clasa care controlează ciclul de viață al procesării cererii, definit de JSF). FacesServlet-ul analizeză cererea și apelează diferite metode în model, folosind bean-uri. Bean-urile, fie ca sunt administrate de JSF, fie că se găsesc în afara contextului, sunt un exemplu de model. Bean-urile ce nu sunt administrate de JSF mediază interacțiunea dintre view și model. Din această cauză, logica de business inclusă în ele trebuie să fie cât mai limitată.
Fig. 5. Modelul arhitectural MVC într-o aplicație web JSF[4]
Am ales implementarea acestui design în aplicația mea, deoarece oferă un mod de dezvoltare foarte flexibil, permițând extinderi ulterioare fără modificări majore ale codului. Ceea ce îl face atât de apreciat de către programatori este separarea celor trei componente, modelul, view-ul și controller-ul, care permite reutilizarea logicii de business în aplicație, dezvoltarea de multiple interfețe grafice, fără a fi afectat codul existent, posibilitatea dezvoltării în paralel a componentelor. Implementarea interfeței grafice poate fi făcută în paralel cu scrierea codului pentru logica de business, de către echipe diferite, specializate în domenii diferite.
Un alt motiv pentru alegerea MVC este acela că, prin modul în care este gândit și construit acest design, devine destul de dificil să scrii cod care să se repte, iar întreținerea codului este mai facilă, tocmai prin organizarea pe care MVC o impune.
2.3.1 Structura proiectului
Pentru o mai clară întelegere a codului, pentru o mai bună organizare, dar mai ales pentru că folosirea design-ului MVC o impune, am structurat resursele proiectului, și anume clasele java, servlet-uri, pagini JSF, fișiere de configurare XML (Extensible Markup Language), și altele, într-o ierarhie de foldere. Deși nu este o cerință obligatorie, am preferat să folosesc o structură de directoare recomandată de standardele Java EE (Enterprise Edition) pentru aplicația mea. De asemenea, această structurare face mai facilă împachetarea și lansarea aplicației pe un server, și poate fi vizibilă în figura de mai jos.
Fig. 6. Structura de directoare a aplicației web
Directorul rădăcină poate avea orice nume, și conține toate fișierele ce ar trebui să fie accesibile aplicației web. Păstrând denumirea implicită, webapp, pagina de pornire a aplicației va avea URL-ul http://localhost:8080/index.xhtml. Toate celelalte pagini se găsesc în subdirectorul pages, fiind împarțite în pagini securizate (folderul secure), accesibile utilizatorilor logați, fie administratori, fie utilizatori normali, și în pagini nesecurizate (folderul unsecure), ce sunt vizibile tuturor vizitatorilor, indiferent daca sunt sau nu logați. Fiecare pagină se va accesa cu URL-ul http://localhost:8080/, urmat de calea spre pagina respectivă (de exemplu, pentru a putea administra conturile utilizatorilor, un administrator logat va trebui sa acceseze link-ul http://localhost:8080/pages/secure/admin/manage-users.xhtml)
Directorul WEB-INF se află chiar sub folderul rădăcină în ierarhia folosită de mine și conține meta-informații. Fișierele stocate aici nu trebuie să fie accesibile din browser, dar aplicația va avea posibilitatea să le acceseze intern, din cod. În această locație am inclus fișierele de configurare necesare aplicației, prin care se realizează integrarea tuturor tehnologiilor și framework-urilor implementate, fișiere ce vor fi prezentate pe larg în capitolul Dezvoltare, la secțiunea Configurări. Cel mai important dintre acestea, și fără de care o aplicație web nu ar putea funcționa, este fișierul web.xml. Rolul său principal este de a specifica informații despre ce servlet-uri vor fi lansate de aplicație și pe ce URL-uri sunt mapate acestea, informații necesare pentru lansarea și executarea aplicației în mod corect.
Directorul resources conține resurse suplimentare utile aplicației mele, cum ar fi scripturi JavaScript și fișiere css (Cascading Style Sheets) pentru stilizarea interfeței grafice.
În ExternalLibraries se află toate JAR-urile (Java Archives), arhive cu biblioteci folosite în aplicație. Câteva exemple ar fi: biblioteca ce pune la dispoziție metode pentru upload-ul de fișiere, biblioteca cu funcții folosite la trimiterea de e-mailuri utilizatorilor, biblioteci pentru interacțiunea cu baza de date și altele.
Directorul src este folderul ce conține toate clasele Java din aplicație. Se poate observa din figura 7 pachetul comun al tuturor claselor, acesta fiind ro.pub.cs.licență, iar structura de directoare subiacentă urmărește principul MVC de separare a celor trei componente, astfel:
modelul, reprezentat de clasele numite entități, ce fac maparea între tabelele din baza de date și obiecte Java, precum și de clasele numite repository, care aduc aceste date în aplicație. Se află in subdirectoarele model și dao (data access objects)
controller-ul, reprezentat de clasele numite controllere, ce primesc cererile de la client, le procesează prin apelarea de servicii din subdirectorul service, și pun mai departe la dispoziția utilizatorilor datele din model, printr-un view. Acestea se gasesc in subfolderul controller
view-ul, care a fost ilustrat în figura anterioară, și care este reprezentat de paginile ce conțin componente JSF prin intermediul cărora care se afișaza utilizatorului, într-o interfață grafică, datele cerute (paginile se găsesc în directorul pages)
Fig. 7. Structura de directoare ce se mapează pe design-ul MVC
2.3.2 Arhitectura 3-tier (pe 3 nivele)
Arhitectura sistemului este una pe trei nivele, bazată pe standardele Java EE, cele trei nivele incluse fiind:
nivelul Persistence (persistența datelor)
nivelul Business
nivelul Prezentare
Modul în care acestea comunică între ele este următorul: primul nivel interacționează cu al doilea, iar al doilea comunică cu ultimul. Astfel, nivelul prezentare nu va executa sau apela niciodată operații din nivelul persistență, în acest mod asigurându-se o arhitectură stabilă.
Nivelul persistență de date realizează operațiile de creare, citire, actualizare și ștergere a datelor (operații CRUD – Create, Read, Update, Delete). În cazul proiectului meu, pentru realiza aceste operații folosesc JPA (Java Persistence API) și Hibernate ca provider al persistenței, împreună cu EntityManager-ul pus la dispoziție de acesta.
Nivelul este împărțit în mai multe clase, iar fiecare clasă se ocupă cu un anumit tip de entități (de exemplu entități legate de categorii, de cursuri sau chiar de conturi ale utilizatorilor) și este utilizată de un singur administrator de entități (EntityManager).
Nivelul business conține toată logica legată de funcționalitatea aplicației web. Funcționalitatea poate reprezenta de exemplu crearea unui utilizator, ștergerea acestuia, adăugarea de cursuri într-o anumită categorie sau modificarea lor de către administrator. Și acest nivel este împarțit în mai multe clase, fiecare dintre acestea fiind adnotată cu „@Service”, adnotare prin care clasele sunt înregistrate în containerul Spring ca bean-uri (componente în nivelul servicii).
În aceste clase se pot accesa datele din nivelul de persistență folosind tot o funcționalitate Spring, aceea de autowire. Clasele și metodele de pe nivelul persistence sunt injectate în servicii prin adnotarea @Autowired, scutind astfel utilizatorul de alte configurări. Datele obținute pot fi prelucrate ulterior, în funcție de ce funcționalitate se dorește de la serviciul respectiv (de exemplu filtrare dupa titlu a cursurilor, filtrare dupa numele de utilizator) și pot fi utilizate mai departe, de nivelul superior.
Prelucrările ce presupun modificarea datelor din baza de date se execută într-o tranzacție, metodele respective fiind adnotate cu „@Transactional”. Astel se asigură integritatea datelor: în cazul în care apare o eroare în sistem iar operația de prelucrare nu s-a încheiat, se face roll back la modificări și datele nu se pierd.
Nivelul prezentare este responsabil cu interfața grafică și afișază informații utilizatorului, construind paginile HTML și primind input de la utilizator prin cereri GET și POST. În aplicație folosesc pagini JSF (ce conțin componente JSF) și Servleturi. Pentru a executa operațiile cerute de utilizator și pentru a primi informațiile ce vor fi afișate în paginile web, la acest nivel se apelează metode din nivelul inferior, cel de business. Aceste informații pot veni sub forma unor șiruri de caractere, în acest caz fiind ușor de afișat, sau pot fi primite date de tipuri mai complexe, cum ar fi entități JPA întregi.
Pentru a ilustra cum se aplică această arhitectură în aplicație, figura 8 prezintă pașii prin care se realizează logarea în aplicație și la ce nivel se execută fiecare. Utilizatorul introduce credențialele în formularul pus la dispoziție de pagina din nivelul de prezentare. Datele introduse ajung la nivelul business, care trebuie să verifice validitatea lor în baza de date. Pe baza datelor primite de la nivelul business, se crează o interogare pe tabela de utilizatori în baza de date. Dacă rezultatul întors este pozitiv și utlizatorul are un cont valid, se trimite răspunsul nivelului de mijloc, pentru a autoriza autentificarea. Prin nivelul prezentare utilizatorul este notificat că s-a autentificat cu succes și i se pot afișa în continuare paginile dorite.
Fig. 8. Exemplu aplicare arhitectura 3-tier în aplicație
Numeroasele avantaje oferite de această arhitectură m-au determinat să o utilizez în aplicația mea:
modul în care se face persistarea datelor în baza de date este unul modular, în cazul în care se dorește folosirea altor tehnologii pentru îndeplinirea acestei funcții, celelalte nivele rămân neafectate
se poate schimba foarte ușor și nivelul prezentare, ceea ce este un avantaj deoarece, la rapiditatea cu care evoluează tehnologiile, cu siguranță pe viitor se va dori înlocuirea lui
faptul că tranzacțiile sunt administrate de containerul aplicației facilitează mult munca programatorului
2.3.3 Stocarea datelor
În această secțiune voi discuta mai în detaliu despre nivelul de persistență a datelor, mai exact despre modul în care se face stocarea și accesarea datelor în aplicația mea. Am folosit o bază de date MySQL, care este o bază de date relațională; asta înseamnă că informațiile sunt structurate în tabele, fiecare tabel conținând la rândul său linii și coloane, iar căutarea datelor se face foarte rapid, pe baza relațiilor între tabele.
Am ales această soluție de stocare întrucât oferă scalabilitate, având capacitatea de stocare pentru o varietate de tipuri de aplicații, de la aplicații embeded, cu doar cațiva MB de date și până la depozite de informații de ordinul TB. În momentul de fața, aplicația mea nu necesită mult spațiu de stocare, dar pe măsură ce se vor uploada cursuri, iar comunitatea de utilizatori va contribui la dezvoltarea platformei, va fi nevoie de o soluție scalabilă pentru stocarea datelor, iar baza de date MySQL este capabilă să răspundă acestor nevoi.
De asemenea, încărcarea datelor se face rapid, datorită memoriilor cache, a indecșilor folosiți în căutări și a altor mecanisme care îmbunătățesc performanța.
Pe lângă scalabilitate și performanță, este foarte important să se păstreze consistența datelor, acest lucru asigurând integritatea aplicației, corectitudinea și completitudinea informațiilor prezentate vizitatorilor. MySQL garantează executarea de tranzacții sigure, ce respectă setul de proprietăți ACID (atomicitate, consistență, izolare și durabilitate).
Unul din cele mai imporante facilități puse la dispoziție de această soluție de stocare este posibilitatea folosirii de conectori și drivere, prin care framework-urile ORM (Object Relational Mapping) pot mapa tabelele din baza de date peste obiecte specifice limbajului Java.
Driverul folosit este JDBC, iar framework-ul care mediază legătura între baza de date și codul Java este Hibernate. Pentru a specifica faptul că o clasă reprezintă corespondentul unei tabele din baza de date, aceasta este adnotată cu „@Entity”, iar pentru a indica tabela în cauză, se folosește adnotarea „@Table(name=”course”). Câmpurile din tabela vor fi reprezentate de campurile din clasa entitate, acestea putând lua orice denumire (nu este obligatoriu să aibă același nume ca cele din baza de date), cu condiția specificării acestui lucru prin adnotarea „@Column(name=”title”)”.
Având de-a face cu o bază de date relațională, este necesară și specificarea în clasele Java a relațiilor dintre tabele (respectiv clase). Hibernate vine în ajutorul programatorilor, printr-un set de adnotări care fac definirea acestor relații foarte ușor de configurat. Mai jos este ilustrat un exemplu de relație între tabela user, ce conține conturile utilizatorilor și tabela role, în care sunt ținute rolurile acestora. Având în vedere că un utilizator poate avea mai multe roluri, iar un rol poate fi deținut de mai mulți utilizatori, avem de-a face cu o relație ManyToMany între cele doua tabele. În codul Java, această legătură este configurată prin adnotările @ManyToMany și @JoinTable, aceasta din urmă indicând numele tabelei de legătură între utilizatori și roluri și coloanele din aceste tabele care vor fi conținute și în tabela de legătură. Această configurare se face atât în clasa ce reprezintă tabela user, UserEntity (figura 9), cât și în clasa RoleEntity (figura 10).
Fig. 9. Configurarea relației ManyToMany în clasa UserEntity
Fig. 10. Configurarea relației ManyToMany în clasa RoleEntity
Un alt tip de relație ce poate exista între tabelele bazei de date este OneToMany. Un exemplu ar fi tabelele category și subcategory: o categorie poate avea mai multe subcategorii. Am reprezentat această legatură în clasele Java prin adnotările @OneToMany și @ManyToOne.
Figura 11 reprezintă modul în care am configurat relația OneToMany în clasa CategoryEntity, corespondenta în Java a tabelei category din baza de date. Având în vedere că o categorie poate avea mai multe subcategorii, în clasa CategoryEntity va exista o listă de obiecte de tipul SubcategoryEntity, adnotate cu „@OneToMany”. Parametrul mappedBy indică obiectul CategoryEntity corespondent pentru care se va face maparea în clasa SubcategoryEntity, iar parametrul cascade specifică ce operații se vor propaga copiilor acestei clase, la persistarea datelor. În acest caz, se vor propaga toate operațiile: salvare, ștergere, detașare, refresh, detașare. De exemplu, când se va șterge o categorie, se vor șterge toți copii aferenți ei: toate subcategoriile și la rândul lor, toți copii subcategoriilor.
Fig. 11. Configurarea relației OneToMany în clasa CategoryEntity
Figura de mai jos ilustrează modul în care se face configurarea relației ManyToOne (mai multe subcategorii corespund unei categorii) în clasa SubcategoryEntity. Trebuie specificat obiectul CategoryEntity ce reprezintă părintele actualei subcategorii, care va fi adnotat cu „@ManyToOne” și „@JoinColumn”. Adnotarea din urmă indică numele coloanei din tabela category ce se va regăsi și în tabela subcategory (mai exact cheia străină).
Fig. 12. Configurarea relației ManyToOne în clasa SubcategoryEntity
Pentru a oferi o înțelegere completă a modului în care se face stocarea datelor în aplicație, și mai ales ce fel de date sunt reținute și care sunt relațiile dintre ele, voi oferi o privire de ansamblu asupra tabelelor din baza de date și a câmpurilor acestora. Aceste informații se pot regăsi și mai jos, în figura 13.
Fig. 13. Tabelele din baza de date și legăturile dintre ele
Tabela user – conține date legate de conturile utilizatorilor, și anume username, e-mail, nume și prenume, parola (care va fi reținută sub o formă criptată), precum și un câmp TOKEN, în care se va memora un cod unic generat atunci când utilizatoul dorește să-și reseteze parola. Acel token va fi atașat unui link și trimis prin email utilizatorului. Data la care este generat va fi memorată în câmpul TOKEN_DATE
Tabela role – conține rolurile ce pot fi atribuite unui utilizator, și anume utilizator normal, cu permisiuni restrânse și administrator, care are drepturi depline
Tabela user_roles – generată automat atunci când s-a definit relația many to many între tabelele user și roles. Aceasta conține asocierile dintre utilizatori și rolul/rolurile fiecăruia
Tabela category – populată cu numele categoriilor adăugate de utilizatori
Tabela subcategory – oferă o ierarhizare a domeniilor existente în aplicație, fiind o ramificare a categoriilor. Conține o cheia straină care face legatura cu tabela category
Tabela course – aici sunt memorate câteva informații legate de cursurile din aplicație, mai exact titlul, tipul conținutului (pdf, video sau text) și nota obținută în urma voturilor utilizatorilor. Este părintele tabelei course_content, ceea ce înseamnă că există cursuri cu diferite tipuri de conținut
Tabela course_content – reține conținutul propriu-zis al cursului, cheia străină prin care se face legătura la cursul parinte, iar în cazul în care este vorba de conținut video, este populat și câmpul lesson_title, acest tip de curs permițând și împarțirea pe lecții.
Tabela rating – conține informații despre nota fiecărui curs, precum și identificatorul utilizatorului care a oferit nota respectivă. În acest mod, mă asigur că un utilizator nu va putea oferi mai multe note aceluiași curs, dar are posibilitatea să își schimbe votul dacă dorește.
2.3.4 Interfața cu utilizatorul
Continuând cu descrierea arhitecturii 3-tier, voi oferi în acest capitol o detaliere a nivelului prezentare, și mai exact a interfeței cu utilizatorul. Acest nivel joacă un rol important într-o aplicație web, întrucât primul contact pe care utilizatorul îl are cu aceasta se face prin paginile sale, prin modul în care acestea sunt afișate, prin facilitatea cu care utilizatorul poate înțelege, naviga și utiliza aplicația.
Prima pagină afișată unui utilizator al site-ului este ilustrată în figura 14 și conține o listă cu ultimele cursuri adăugate în aplicație. De asemenea, în partea laterală stânga se poate observa meniul, ce conține toate categoriile, subcategoriile și cursurile existente, fiecare nivel putând fi expandat sau restrâns pentru a salva spațiul de afișare. Acest meniu este prezent în toate paginile aplicației, astfel încât utilizatorul poate accesa și vizualiza un curs din orice pagină s-ar afla. Am ales sa fac meniul vizibil în toate paginile pentru a oferi o navigare cât mai rapidă către informațiile de care utilizatorul are nevoie. De asemenea, ierarhizarea conținutului permite o căutare ușoară a cursurilor, în funcție de domeniul din care acestea fac parte.
Fig. 14. Pagina de pornire a aplicației
Ce mai importantă pagină a aplicației este cea de vizualizare a cursurilor, ea indeplinind de fapt scopul aplicației, și anume informarea utilizatorilor. De aceea, am gândit această pagină astfel încât să satisfacă nevoile mai multor tipuri de utilizatori, prin suportul pentru afișarea mai multor tipuri de conținut. Am luat în calcul mai multe opțiuni, deoarece pot exista utilizatori ce învața mai bine din tutoriale video, iar alții din conținut text, scris. Astfel, aplicația suportă atât cursuri scrise, în format text sau fișiere pdf, cât și cursuri video, structurate în lecții.
Mai jos este prezentată pagina de afișare a cursurilor, în partea stângă având un exemplu de curs în format pdf, iar în partea dreaptă un exemplu de tutorial video. Cursurile nu vor putea fi descărcate, întrucît reprezintă proprietatea autorilor.
Fig. 15. Exemple de pagini de vizualizare a cursurilor
O altă serie importantă de pagini conține paginile afișate utilizatorului pentru autentificarea în aplicație. Pentru a preveni adăugarea de conținut eronat de către utilizatori neinformați, am restricționat posibilitatea adăugării cursurilor doar pentru utilizatori ce au un cont valid și sunt autentificați. Astfel, atunci când un vizitator dorește să contribuie cu informațiile sale la îmbogățirea conținutului aplicației, este redirecționat catre o pagină de autentificare, unde trebuie să introducă un nume de utilizator și o parolă (figura 16, partea stângă). După adăugarea cursului, numele utilizatorului și data la care a fost adăugat vor apărea în colțul dreapta jos al paginii de vizualizare a cursurilor. Astfel, în cazul în care conținutul adăugat nu este unul corespunzător, administratorul va putea restricționa respectivul utilizator sau chiar îi va putea șterge contul.
Dacă utilizatorul și-a uitat parola, are posibilitatea de a o reseta (figura 16 partea dreaptă). Pentru a realiza acest lucru, trebuie sa introducă o adresă de e-mail validă, mai exact adresa de e-mail cu care s-a înregistrat inițial în aplicație. Pentru resetarea parolei, va primi prin e-mail un link de resetare, valabil 24 de ore, care îl va direcționa către pagina din figura 17, partea stângă. Aici va trebui sa introducă de două ori noua parolă și să se autentifice din nou în aplicație, folosindu-se de aceasta.
Fig. 16. Paginile de autentificare, respectiv resetare a parolei
Fig. 17. Pagina de introducere a parolei noi (stânga) și pagina de creare cont utilizator (dreapta)
Pentru a putea popula aplicația cu conținut necesar utilizatorilor, este nevoie de o pagină de adăugare a cursurilor. După cum am menționat mai sus, această pagină este vizibilă doar utilizatorilor autentificați. Pentru a adăuga un curs este necesară selectarea categoriei și subcategoriei din care face parte cursul. Dacă nu există o categorie sau o subcategorie adecvată în lista pusă la dispoziție, utilizatorul are posibilitatea de a a dăuga propriul domeniu din care face parte cursul, prin cele două butoane, Add new category, respectiv Add new subcategory. De asemenea trebuie introdus un titlu pentru curs, și apoi selectat tipul de conținut: text, pdf sau tutorial video. În figura de mai jos este prezentat un exemplu de adăugare a unui curs video, în care se poate observa lista de lecții cu titlul și clipul video al fiecareia, precum și butonul de adăugare de lecții noi, Add Lessons.
Fig. 18. Pagina de adăugare a unui curs
Asigurarea integrității și corectitudinii informațiilor ce sunt adăugate în aplicație este o cerință obligatorie. De accea administratorul trebuie să dețină dreptul de ștergere, modificare sau completare a conținutului cursurilor adăugate de utilizatori, acest lucru fiind posibil din pagina de editare a cursurilor. Din pagina de vizualizare a cursurilor, administratorul are la dispoziție un buton prin care este redirecționat către editarea cursului respectiv, această pagină fiind ilustrată in figura de mai jos. În partea stângă este un exemplu de editare a unui curs în format text, iar în partea dreaptă este prezentată pagina de editare a unui tutorial video. În ambele exemple, administratorul are drept deplin asupra conținutul, putând schimba titlul, porțiuni din text, putând adăuga sau șterge lecții.
Fig. 19. Exemple de pagini de editare a cursurilor
O altă pagină vizibilă doar administratorului este aceea de management al utilizatilor, care poate fi accesată prin butonul Management Utilizatori. După cum se poate observa și în figura 20, administratorul va vedea în această pagină o listă cu toate conturile utilizatorilor și detalii despre aceștia: numele de utilizator, numele, prenumele, adresa de email. În cazul în care aplicația va crește și se vor înregistra foarte mulți utilizatori, există funcționalitatea de paginare a listei conturilor acestora, pentru a face mai rapidă navigarea pentru administrator. De asemenea, se poate filtra lista, permițându-se căutarea unui utilizator dupa detaliile afișate în pagină.
Administratorul poate modifica informațiile despre fiecare cont și de asemenea, poate șterge contul unui utilizator. Am făcut posibil acest lucru, pentru a putea elimina conturile persoanelor ce încarcă în aplicație conținut necorespunzător. La ștergere va apărea un dialog de confirmare, pentru a împiedica ștergerea unui utilizator dintr-o eroare.
În partea de jos a liste există butonul Create User, care dă posibilitatea administratorului să adauge noi conturi de utilizatori, formularul de adăugare fiind similar cu cel prezent în figura 17 (partea dreaptă), și anume pagina de înregistrare a unui utilizator în aplicție.
Fig. 20. Pagina de administrare a utilizatorilor
3. Dezvoltare
Acest capitol prezintă pașii urmați pentru dezvoltarea aplicației web, începând de la alegerea tehnologiilor folosite și continuând cu modul în care acestea se integrează, folosind fișierele de configurare.
3.1 Tehnologii folosite
Alegerea tehnologiilor este un pas important în implementarea oricărei aplicații, deoarece de acestea va depinde performanța sistemului, ușurința în dezvoltare, dar și gradul de flexibilitate oferit în realizarea funcționalităților dorite.
Am optat pentru stack-ul de tehnologii și framework-uri descris mai jos, întrucât se pretează cel mai bine pentru implementarea unei aplicații web folosind limbajul de programare Java, și, de asemenea, simplifică procesul de dezvoltare și structurează întregul proiect într-un mod consistent și productiv.
3.1.1 Aplicații web
Fig. 21. Funcționarea unei aplicații web
Figura de mai sus prezintă funcționarea unei aplicații web. O aplicație web este un program software care rulează pe un server web. Spre deosebire de aplicațiile desktop tradiționale, care sunt pornite de sistemul de operare, aplicațiile web trebuie accesate prin intermediul unui browser web, permițând accesul de pe mai multe computere diferite, la datele aflate pe serverul bazei de date, fără a fi nevoie de distribuirea acestora pe fiecare platformă în parte.
3.1.2 Framework-ul Spring
Framework-ul Spring este o platformă ce oferă suport complet pentru infrastructura aplicațiilor Java, facilitând munca dezvoltatorului, care astfel nu va mai fi preocupat de infrastructură, ci doar de aplicația implementată.
De asemenea, Spring permite dezvoltarea aplicațiilor pornind de la POJO-uri (Plain Old Java Objects), peste care aplică în mod neinvaziv servicii enterprise; această capabilitate este valabilă atât pentru modelul de programare Java SE (Standard Edition) cât și parțial pentru modelul Java EE (Enterprise Edition). Câteva exemple care demonstrează beneficiile aduse unui dezvoltator de platforma Spring sunt următoarele:
executarea metodelor Java într-o tranzacție în baza de date nu mai necesită interacțiunea cu API-ul (Application Programming Interface) tranzacției
construirea procedurilor la distanța pornind de la metode Java nu necesită interacțiunea cu API-uri remote
implementarea de metode Java locale având funcționalitățile unor operații de management nu necesită interacțiunea cu API-ul JMX (Java Management Extensions)
implementarea de metode Java locale cu funcționalitățile unor handlere de mesaje nu necesită utilizarea API-ului JMS (Java Message Server)[5]
Orice aplicație Java constă, în general, din obiecte ce comunică între ele, pentru a constitui rezultatul final prezentat utilizatorului. Atunci când se dezvoltă aplicații complexe, clasele trebuie să fie cât mai independente posibil între ele, pentru a putea fi reutilizate și testate individual în cadrul unit testelor. Una din funcționalitățile cele mai importante ale Spring este „Dependency Injection” (Injecția de dependențe) care contribuie la unificarea acestor clase, dar în același timp păstrându-le independente.
Fig. 22. Arhitectura Spring[5]
Figura 22 prezintă modulele si funcționalitățile incluse în arhitectura Spring, doar o parte din acestea fiind incluse în proiectul dezvoltat de mine:
Spring Core – modulele utilizate pun la dispoziție elementele fundamentale ale framework-ului, injecția de dependențe fiind cea mai intens folosită în proiect
Spring Context – utilizat pentru integrarea bibliotecilor de mailing (Java Mail) în aplicație, necesare la trimiterea de e-mailuri utilizatorilor, pentru resetarea de parole
Spring JDBC – conferă un nivel de abstractizare peste JDBC (Java Database Conectivity), evitându-se scrierea și parsarea greoaie de cod necesar conectivității cu baza de date
Spring ORM – modulul furnizează layere de integrare cu API-uri ORM (Object Relational Mapping) populare, inclusiv JPA (Java Persistence API) și Hibernate, folosite de mine în aplicație
3.1.3 Baza de date MySQL
Orice aplicație web are nevoie de date și de o locație în care să le stocheze. Într-o aplicație foarte complexă, de obicei datele se află într-o bază de date, pe un server diferit de cel al aplicației, dar în cazul proiectului de față, acesta va fi instalat împreună cu baza de date, pe același server.
Am ales MySQL pentru stocarea datelor aplicației mele, întrucât este un sistem open source de management al bazelor de date foarte cunoscut și foarte des folosit în aplicații web datorită vitezei, flexibilității și fiabilității sale. MySQL folosește SQL (Structured Query Language) pentru a accesa și procesa datele stocate în baza de date.
Comunicarea între limbajul înțeles de serverul bazei de date și codul Java, înțeles de serverul web, se face printr-un driver pus la dispoziție de MySQL, și anume MySQL Connector, care implementează API-ul JDBC. În plus, pentru conexiunea la baza de date, mai este nevoie de un URL al acesteia, numele de utilizator și parola pentru accesul la date.
Fig. 23. Conexiunea aplicație web – bază de date
Figura 23 descrie pașii prin care o aplicația se conectează la baza de date MySQL:
aplicația Java folosește API-ul JDBC, care pune la dispoziție un set de interfețe și metodele acestora, pentru a permite accesul la baza de date
API-ul JDBC utilizează un driver manager și alte drivere specifice bazei de date pentru a furniza conectivitate transparentă la multiple tipuri de baze de date
JDBC driver managerul asigură la rândul său folosirea driverului corect pentru accesul la diferite surse de date. Driver managerul poate suporta mai multe drivere concurente, conectate la diferite tipuri de baze de date
3.1.4 Hibernate
În sistemele orientate obiect, cum sunt aplicațiile Java, există o asimetrie între modelul obiect și baza de date relațională. Bazele de date relaționale (RDBMS – Relational Database Management System) reprezintă datele într-o formă tabelară, în timp ce în limbajele orientate obiect, datele sunt reprezentate sub forma unui graf interconectat de obiecte. O tehnică de programare prin care datele dintr-o bază de date relațională sunt transformate în obiecte compatibile cu limbajele de programare orientate obiect este ORM (Object Relational Mapping). Există mai multe framework-uri și opțiuni ORM în Java, una din ele fiind Hibernate, soluția aleasă de mine.
3.1.4.1 Roluri
Hibernate este un framework open source pentru limbajul Java, puternic și performant, care pe lângă maparea claselor peste tabelele din baza de date și maparea tipurilor de date Java peste tipurile de date SQL, oferă și facilități de interogare și extragere a datelor. Astfel, elimină din munca dezvoltatorului 95% din task-urile de programare asociate cu persistarea datelor in baza de date.
Fig. 24. Rolul Hibernate într-o aplicație web[7]
Figura de mai sus ilustrează rolul framework-ului Hibernate într-o aplicație web. Hibernate se poziționează între obiectele Java și serverul bazei de date, asigurând persistarea acestor obiecte pe baza mecanismelor și modelelor ORM corespunzătoare .
Alte roluri ale Hibernate:
maparea claselor peste baza de date se poate face în fișiere XML (Extensible Markup Language), fără a mai fi nevoie să se scrie cod. Astfel, dacă apar modificări în orice tabel din baza de date, se va modifica în mod corespunzător doar fișierul XML asociat
nu necesită un server de aplicații pentru a funcționa
manipulează asocieri complexe între tabelele bazei de date
diminuează accesul la baza de date prin strategii rapide de extragere a datelor
pune la dispoziție interogări cât mai simple ale datelor
3.1.4.2 Arhitectura Hibernate
Arhitectura Hibernate este una stratificată, pentru a ascunde programatorului detalii despre API-urile subiacente. Framework-ul utilizează baza de date și fișierele de configurări pentru a pune la dispoziție aplicației servicii și obiecte de persistență a datelor. Mai jos este prezentată arhitectura detaliată a unei aplicații ce folosește Hibernate, precum și câteva clase importante.
Fig. 25. Arhitectura Hibernate în aplicații Java[7]
Hibernate utilizează diferite API-uri Java, cum ar fi JDBC, JTA (Java Transaction API) sau JNDI (Java Naming and Directory Interface):
JDBC oferă un nivel elementar de abstractizare a funcționalităților caracteristice bazelor de date relaționale, asigurând suportul pentru Hibernate aproape oricărei baze de date ce are un driver JDBC
JNDI și JTA permit integrarea Hibernate cu serverele J2EE (Java 2 Platform, Enterprise Edition)
O scrută descriere a claselor ce fac parte din arhitectura unei aplicații Hibernate:
Configuration este primul obiect Hibernate creat în orice aplicație ce folosește acest framework, fiind creat în general o singură dată, la inițializarea aplicației. Acesta reprezintă de fapt un fișier de configurare sau de proprietăți, necesar Hibernate, și pune la dispoziție două componente cheie: conexiunea cu baza de date și configurările pentru maparea claselor
SessionFactory este creată prin intermediul clasei Configuration și are rolul de a seta Hibernate folosind fișierele de configurări și de a instanția un obiect Sesiune. SessionFactory este un obiect trade safe, ceea ce înseamnă că poate fi utilizat în siguranță de toate threadurile din aplicație. Clasa este instanțiată la pornire, fiind necesar câte un obiect SessionFactory pentru fiecare bază de date, deoarece se folosesc fișiere de configurare diferite
Session (obiectul Sesiune) este utilizat pentru a obține conexiunea fizică cu baza de date și va fi instanțiat la fiecare interacțiune cu aceasta. De asemenea, prin intermediul Sesiunii sunt salvate și extrase obiectele persistente. Este indicat ca obiectele Sesiune să nu rămâne deschise pentru o perioadă lungă, deoarece nu sunt thread safe
Transaction (obiectul Tranzacție) reprezintă o unitate de lucru cu baza de date, majoritatea RDBMS suportând această funcționalitate. În Hibernate, tranzacțiile sunt manipulate de un manager de tranzacții de pe un nivel inferior (JDBC sau JTA) și sunt obiecte opționale, aplicațiile Hibernate putând administra tranzacțiile și fără acestea, doar din cod
Query (obiectul Interogare) folosește SQL sau HQL (Hibernate Query Language) pentru a extrage datele din baza de date și pentru a crea obiecte din acestea. O instanță Query face legătura între parametri unei interogări, limitează numărul de rezultate adus de acea interogare și, nu în ultimul rând, execută interogarea.
Criteria (obiectul Criteriu) este utilizat pentru a crea și executa interogări pe baza unor criterii și pentru a obține rezultatele sub formă de obiecte
3.1.5 JSF
Ca în orice aplicație web, interfața grafică este foarte importantă, reprezentând primul contact pe care utilizatorul îl are cu aplicația accesată. Designul acesteia are scopul de a spori eficiența și ușurința în utilizare, experiența utilizatorului cu aplicația determinându-l să revină sau nu pe viitor.
Pentru construirea interfeței am folosit JSF (Java Server Faces), un framework web care simplifică implementarea ecranelor aplicației, prin reutilizarea componentelor UI (User Interface) în interiorul paginilor. JSF oferă facilități de conectare a widget-urilor UI cu datele din baza de date și cu elementele de tratare a evenimentelor pe partea de server și pune la dispoziția programatorului un API bogat pentru dezvoltarea componentelor.
Am ales acest framework deoarece aduce multiple beneficii unui programator, reducând efortul de implementare și întreținere a aplicațiilor Java ce rulează pe un server și redau clienților interfața grafică. JSF facilitează dezvoltarea aplicațiilor web prin:
folosirea componentelor UI reutilizabile
ușurința în transferul de date între componentele UI
managementul stării interfeței peste mai multe cereri ale serverului
implementarea de componente noi
legătura între evenimentele pe partea de client și codul aplicației de pe server
3.1.5.1 Arhitectura JSF
Figura 26 prezintă arhitectura unei aplicații JSF, care este similară oricărei alte aplicații web ce folosește tehnologii Java: aceasta rulează pe un server și este compusă din:
obiecte JavaBean ce conțin funcționalități și date specifice aplicației
o bibliotecă de etichete pentru reprezentarea tratării de evenimente și validatorilor
componente UI, reprezentate ca obiecte stateful (care își păstrează starea) pe server
clase ajutătoare pe partea de server
validatori, tratări de evenimente și handlere de navigare
fișier de configurare a resurselor aplicației
Fig. 26. Arhitectura unei aplicații ce folosește JSF
Controllerele sunt folosite pentru a executa acțiuni ale utilizatorilor, componentele JSF fac parte din paginile ce compun interfața grafică, iar JavaBean-urile includ logica de business. Afișarea componentelor UI se poate face prin mai multe mecanisme puse la dispoziție de JSF, metoda aleasă depinzând de preferințele designerului web și neinfluențând dezvoltatorul.
3.1.5.2 Ciclul de viață al unei aplicații JSF
Așa cum este ilustrat și în figura 27, o aplicație JSF trece prin 6 stări de-a lungul existenței sale, pentru exemplificare folosindu-se fazele procesării unui formular de către JSF. Cele 6 faze sunt:
Restaurarea View-ului
Aplicarea valorilor din cerere
Procesarea validărilor
Actualizarea valorilor din model
Invocarea aplicației
Afișarea răspunsului
În figură sunt prezentate atât cele 6 faze (în nuanța albastru deschis) cât și procesarea de evenimente ce are loc la finalul fiecărei faze (în nuanța albastru închis).
Fig. 27. Ciclul de viață al unei aplicații ce folosește JSF pentru procesarea unui formular[8]
Faza 1: Restaurarea view-ului
Această fază începe imediat după apăsarea unui link sau a unui buton din interfață, în urma căreia JSF primește o cerere. În timpul acestei faze, JSF construiește view-ul, leagă tratarea evenimentelor și validatorii de componentele UI și salvează ecranul în instanța unui obiect FacesContext. Obiectul FacesContext va conține acum toate informațiile necesare procesării unei cereri.
Faza 2: Aplicarea valorilor din cerere
După crearea (sau restaurarea) arborelui de componente, fiecare dintre acestea folosește metode de decodificare pentru a extrage noua sa valoare din parametrii cererii și stochează această valoare. Dacă procesul de conversie eșuează, va fi generat un mesaj de eroare, care va fi pus în așteptare pe o coadă din instanța FacesContext. În faza de afișare a răspunsului va fi afișat acest mesaj de eroare, împreună cu alte erori de validare.
Dacă vreuna dintre metodele de decodificare sau vreun ascultător de evenimente a apelat funcția renderResponse() în instanța curentă FacesContext, JSF trece la faza de afișare a răspunsului.
Faza 3: Procesarea validărilor
În această fază, JSF procesează toate validările înregistrate în arborele de componente. Pentru validare, se analizează toate regulile atributelor specifice unei componente și se compară aceste reguli cu valorile stocate în componentă.
Dacă valoarea locală este invalidă, JSF adaugă în coada obiectului FacesContext curent un mesaj de eroare, iar ciclul de viața trece la faza de afișare a răspunsului, redând utilizatorului aceeași pagină, împreună cu mesajele de eroare din coadă.
Faza 4: Actualizarea valorilor din model
După ce JSF verifică validitatea datelor, parcurge arborele de componente și setează proprietățile obiectelor de pe server la valorile componentelor locale. Se vor actualiza de asemenea și proprietățile din bean-uri, corespunzătoare valorilor atributelor componentelor din input.
Dacă vreuna din metodele updateModels() apelează renderResponse() în instanța curentă FacesContext, JSF trece în faza de afișare a răspunsului.
Faza 5: Invocarea aplicației
Pe parcursul acestei faze, JSF tratează toate evenimentele apărute la nivel de aplicație, cum ar fi trimiterea unui formular sau accesarea unei alte pagini printr-un link.
Faza 6: Afișarea răspunsului
În această fază, JSF cere serverului aplicației să afișeze pagina, dacă aplicația folosește pagini JSP (Java Server Pages). În cazul în care se execută o cerere inițială, componentele reprezentate în pagină vor fi adăugate în arborele de componente, atunci când containerul JSF execută pagina. Dacă nu este vorba de cererea inițială, arborele de componente este construit deja, așa ca nu mai este nevoie de adăugarea din nou a componentelor. În oricare din cele două cazuri, componentele se vor afișa pe măsură ce serverul aplicației sau containerul JSP parcurge etichetele din pagină.
După ce pagina este afișată, starea răspunsului se salvează, pentru ca cererile următoare să o poată accesa sau să poată fi folosită in faza de restaurare a view-ului.
3.1.6 Primefaces
Pentru extinderea componentelor sale și pentru crearea de pagini responsive, JSF utilizează biblioteci de componente. Mai jos este o lista cu câteva din bibliotecile ce pot fi folosite împreună cu JSF:
RichFaces
ICEFaces
ADF Faces
PrettyFaces
OpenFaces
OmniFaces
PrimeFaces
În implementarea proiectului am ales Primefaces, care este o bibliotecă open source, compatibilă JSF 2.0, ce extinde JSF cu peste 100 de componente. Una din cele mai utile funcționalități Primefaces este suportul pentru crearea de pagini responsive, care a devenit un „must have” în ziua de astăzi pentru paginile web.
Beneficiile aduse de această bibliotecă și motivele pentru care am preferat această soluție în detrimentul celorlalte, sunt listate mai jos:
conține un set bogat de componente UI (câteva exemple ar fi: DataTable – pentru tabele de date, AutoComplete, HTMLEditor, Grafice etc.)
nu necesită configurări XML suplimentare sau alte dependențe
vine cu Ajax integrat, bazat pe standardul API-urilor Ajax JSF 2.0
documentație foarte bine scrisă, cu multe exemple de cod
ușor de utilizat
3.1.7 Apache Tomcat
Pentru a primi cereri și a trimite răspunsuri clienților, aplicațiile web au nevoie de un server web. Apache Tomcat este cel mai popular server de aplicații web, înregistrând peste un milion de descărcări lunar, și o răspândire de peste 70% în centrele de date enterprise. Tomcat este folosit în orice tip de aplicații web, de la site-uri simple, ce se află pe un singur server, și până la rețele întregi din cadrul companiilor.
Apache Tomcat este o implementare foarte răspândită a specificațiilor Java Servlet, care a fost lansată ca un proiect open source de către ASF (Apache Software Foundation) în 1999, atunci când sursele proiectului au fost donate ASF de către Sun Microsystems. Este dezvoltat de un grup de programatori dedicați și, ca orice alt produs al ASF, este 100% gratuit, indiferent dacă este folosit pentru dezvoltarea altor software-uri open source sau ca parte a unor software-uri proprietare.
3.1.7.1 Motivație
Există o mulțime de servere de aplicații ce pot fi folosite în lansarea unei aplicații web, însă am ales Apache Tomcat din următoarele motive:
este un container de servleturi rapid și ușor de folosit, extrem de bine optimizat. În domeniul servirii paginilor JSP, subclasează orice altă soluție open source, inclusiv Geronimo sau Jboss
este flexibil și scalabil, fiind preferat de dezvoltatori deoarece rulează foarte repede aplicațiile. Administratorii preferă de asemenea Tomcat, întrucât permite construirea unei infrastructuri de proiect începând de la bază spre vârf și customizarea acesteia după nevoile fiecăruia
securitatea: deși nu există noțiunea de securizare perfectă, un istoric perfect este posibil. Până acum nu s-a înregistrat niciun caz de apariție a vreunei breșe de securitate in aplicațiile ce folosesc Tomcat.
3.1.7.2 Cum funcționează
Tomcat are la bază mai multe componente, incluzând motorul Tomcat JSP și o varietate de conectori, dar componenta sa principală este Catalina. Acesta este un container de servlet-uri, care pune la dispoziția Tomcat implementarea specificațiilor servlet; la pornirea Tomcatului, se pornește de fapt Catalina.
Un container de servleturi este un sistem complex, a cărui funcționalitate poate fi sumarizată în trei acțiuni pe care acesta le realizează pentru tratarea cererilor pentru un servlet:
creează un obiect cerere și îl populează cu informații ce vor fi utile servlet-ului invocat, cum ar fi parametrii, headere, cookie-uri, interogări, URI-uri (Uniform Resource Identifier). Obiectul cerere este o instanță a interfeței javax.servlet.ServletRequest sau javax.servlet.http.ServletRequest
creează un obiect răspuns pe care servlet-ul invocat îl folosește pentru a trimite răspunsul clientului web. Obiectul răspuns este o instanță a interfeței javax.servlet.ServletResponse sau javax.servlet.http.ServletResponse.
invocă metoda serviciu a servletului, transmițând ca parametrii obiectele cerere și răspuns. În acest moment servletul citește valorile din cerere și le scrie în obiectul răspuns
3.1.7.3 Arhitectura
În ceea ce privește arhitectura, Tomcat constă într-o ierarhie de componente imbricate, containerele reprezentând la rândul lor componente ce pot conține colecții de alte componente.
Figura de mai jos ilustrează arhitectura Apache Tomcat, componentele ce pot exista de mai multe ori într-un container fiind reprezentate în imagine de simbolul ce sugerează multiple profile. Exemplu: Connector, Logger, Service etc.
Fig. 28. Arhitectura Apache Tomcat[11]
În cele ce urmează, voi descrie pe scurt fiecare componentă.
Serverul: este reprezentat chiar de Tomcat și este o instanță a serverului de aplicații web. Acesta deține un port folosit pentru închiderea serverului (portul 8005). Pot exista mai multe servere pe un nod, cu condiția ca fiecare să folosească porturi diferite. Serverul este o implementare a interfeței Server, implementată în obiectul StandardServer.
Serviciul: un serviciu grupează un container (de obicei un motor) împreună cu un set de conectori. Serviciul este responsabil pentru primirea de cereri, direcționându-le apoi către aplicația web corespunzătoare și spre resursele specifice, întorcând rezultatul la finalul procesării cererii. Astfel, serviciul este intermediarul între client, reprezentat de browserul web și container.
Conectorii: au rolul de a conecta aplicația la clienți. Aceștia primesc cereri HTTP (Hypertext Transfer Protocol) pe portul 8080 sau cereri AJP (Apache JServ Protocol) pe portul 8009. Conectorul implicit este Coyote, care implementează HTTP 1.1
Motorul: motorul este containerul de nivel înalt, și nu poate fi conținut de un alt container, ceea ce înseamnă că este containerul părinte pentru toate celelalte elemente aflate pe nivele mai joase. Motorul este o componentă ce procesează cereri, reprezentând Catalina Server Engine.
Rolul său este de a analiza headerele HTTP și a determina gazda virtuală sau contextul către care trebuie trimisă cererea. Un motor poate conține host-uri reprezentând un grup de aplicații web și contexte reprezentând o singură aplicație (exemplu: un host virtual).
Realm: această componentă administrează autentificarea și autorizarea utilizatorilor în interiorul întregului motor. Acest comportament poate fi totuși suprascris, prin utilizarea unei componente Realm la nivel de host sau context.
Valve: componentă folosită pentru a intercepta o cerere și a o preprocesa. Funcționează similar mecanismului de filtrare din specificațiile servlet-urilor, dar este caracteristic Tomcat. Este utilizată pentru logarea o singură dată a tuturor hosturilor de pe server, pentru logarea modelelor de cereri, a adreselor IP ale clienților, a modelelor de utilizare a serverului. Pot exista mai multe astfel de componente pentru un singur părinte, și sunt de obicei înlănțuite în ordinea în care au fost adăugate în acel părinte.
Loggeri: loggerii raportează starea internă a unei componente. Comportamentul de logare este moștenit, ceea ce înseamnă că logger-ul ce există in motor este alocat fiecărui copil, exceptând cazurile în care este suprascris.
Host: hostul simulează cunoscutul concept de gazdă virtuală Apache și conține un nume și o adresă IP. Pot exista mai multe host-uri, fiecare cu propria aplicație web.
Contextul: reprezintă de fapt aplicația web. Din context se poate activa reîncărcarea dinamică, astfel încât toate clasele care au fost modificate să fie reîncărcate în memorie, iar contextul poate avea de asemenea pagini de eroare specifice fiecărei aplicații web. Controlul accesului poate fi setat prin parametri de inițializare. Contextul implementează interfața Context, majoritatea implementărilor folosind clasa StandardContext.
Deoarece această componentă este ea însăși un container la nivel de aplicație web, poate fi considerată părintele servleturilor si filtrelor, pe care și le adaugă drept clase StandarWrapper.
3.2 Configurări ale proiectului
Pentru a putea integra în proiect și a lega între ele toate tehnologiile și framework-urile prezentate mai sus, este de nevoie de fișiere de configurări. În continuare voi prezenta câteva din cele mai importante fișiere de configurări necesare aplicației web implementată de mine, împreună cu exemple extrase din aceste fișiere și explicații pentru fiecare.
3.2.1 web.xml
Toate aplicațiile web scrise în Java folosesc un fișier descriptor pentru deploy (lansarea aplicației), care specifică cum se mapează URL-urile(Uniform Resource Locators) peste servlet-uri, care URL-uri necesită autentificare, precum și alte informații. Acest fișier este denumit web.xml și este localizat în folderul war al aplicației, în subfolderul WEB-INF. După cum îi sugerează și numele, este un fișier xml, al cărui element rădăcină este tagul <web-app>.
Web.xml face parte din standardul servlet pentru aplicații web și descrie clasele, resursele, configurarea aplicației și modul în care serverul web folosește această configurare pentru a deservi cereri web. Mai exact, serverul web identifică ce servlet trebuie să trateze o anumită cerere și apelează metoda corespunzătoare respectivei cerei (de exemplu, metoda doGet() este apelată petru cereri HTTP GET).
Maparea URL-urilor peste servleturi se face prin declararea servletului cu eticheta <servlet>, urmată de definirea mapării URL-ului peste servletul declarat, cu eticheta <servlet-mapping>. Elementul <servlet> declară servletul, incluzând un nume, folosit ulterior pentru referirea respectivului servlet de către alte elemente din fișier, clasa utilizată pentru servlet și parametri necesari inițializării. Numele trbuie să fie unic pentru fiecare servlet. Elementul <servlet-mapping> specifică tiparul URL-ului și numele servletului folosit pentru cereri ale caror URL-uri se potrivesc tiparului declarat. Se poate utiliza un asterisk (*) la începutul sau la sfârșitul tiparului, pentru a indica existența a zero sau mai multe caractere de orice tip.
Fig. 29. Declararea unui servlet in fișierul web.xml
Figura 29 prezintă declararea servletului FacesServlet. Una din cerințele aplicațiilor JSF este ca toate cererile ce fac referire la componente JSF anterior salvate, să treacă prin acest servlet. O instanță a acestui servlet administrează ciclul de viață al porcesării cererii și inițialaizeză resursele necesare JSF. Înainte ca prima pagină web să fie accesată, containerul web trebuie să invoce instanța FacesServlet pentru a începe ciclul de viață al aplicației.
Pentru a mă asigura că instanța FacesServlet este invocată, este necesară și configurarea mapării. Am folosit maparea prin extensie, prin care se specifică faptul că paginile au conținut JSF. Când o cerere pentru o pagină cu extensia .xhtml ajunge la server, containerul va trimite cererea instanței FacesServlet, care se așteaptă să existe o pagină corespunzătoare, cu același nume.
Filtrele sunt clase care se comportă similar servlet-urilor în ceea ce privește cererile, dar permit continuarea tratării cererii de către alte servleturi sau filtre. Un filtru poate îndeplini sarcini suplimentare înainte de a apela servletul, cum ar fi logarea, verificări de autentificare speciale, adnotarea obiectelor cerere sau răspuns. De asemenea, permite construirea de sarcini de procesare a cererilor din fișierul web.xml.
O clasă filtru implementează interfața javax.servlet.Filter, inclusiv metoda doFilter().
Mai jos sunt descrise două filtre ce au fost necesare în aplicația mea, mai exact FileUploadFilter și DelegatingFilterProxy. Primul dintre acestea parsează cererile multipart, iar cel de-al doilea are rolul de a căuta și invoca un bean din contextul Spring.
Filtrele sunt declarate prin eticheta <filter>, pentru care se specifică un nume unic și clasa respectivului filtru. Elementul <filter-mapping> conține un <filter-name>, reprezentat de numele unui filtru declarat anterior și una din etichetele: <url-pattern>, care specifică un tipar de URL pentru care se va aplica filtrul, respectiv <servlet-name>, element care indică numele unui servlet declarat anterior, pentru fiecare apelare a servletului aplicându-se filtrul.
Fig. 30. Declararea filtrellor în aplicație
Când un container de servleturi apelează o metodă dintr-un servlet în locul clientului, cererea HTTP trimisă de client este transmisă implicit către servlet. Răspunsul generat de servlet este, implicit, transmis direct înapoi la client, având conținutul nemodificat de container. Însă există cazuri în care este nevoie de preprocesări ale cererii înainte de a ajunge la servlet, sau de modificări ale răspunsului (de exemplu criptarea și decriptarea datelor). Avantajul filtrelor apare atunci când se dorește aplicarea unor preprocesări sau postprocesări ale cererii sau răspunsului pentru un grup de servleturi (pentru un singur servlet, modificările se pot face direct în implementarea servletului).
Fig. 31. Comparație configurare aplicație fără filtre (stânga) și cu filtre (dreapta) [12]
Figura 31 ilustrează cum se desfășoară invocarea filtrelor de către container. În partea stângă este prezentat un scenariu în care nu sunt configurate filtre, pe când în partea dreaptă este configurat un lanț de filtre, care va fi invocat de container inainte de apelarea servletului și după ce acesta a trimis răspunsul. Ordinea în care sunt declarate filtrele in fișierul web.xml contează, aceasta fiind și ordinea în care vor fi invocate.
3.2.2 applicationContext.xml
În aplicațiile Srping există două tipuri de containere, fiecare configurat și inițializat diferit. Unul este Application Context, iar celălat Web Application Context. În continuare voi prezenta prima versiune de container, cea utilizată de mine în proiect.
Application Context-ul este de fapt containerul care leagă între ele beanurile dintr-o aplicație Spring. Aceste beanuri interacționează pentru a oferi servicii aplicației și poartă numele de colaboratori.
Containerul Application Context este inițializat de un ContextLoaderListener, definit în fisierul web.xml. Mai jos este ilustrat modul în care se face definirea: prin elementul <context-param> se face o cerere către Spring pentru încărcarea fișierului applicationContext.xml și crearea containerului pornind de la acesta. Contextul poate conține component precum servicii tranzacționale, obiecte pentru accesul la date sau alte obiecte ce ar putea fi utilizate și reutilizate în aplicație. Fiecare aplicație va avea un Application Context.
Fig. 32. Inițializarea containerului Application Context
ContextLoaderListener-ul citește fișierul de configurare Spring, și anume applicationContext.xml, îl parsează și încarcă beanurile definite acolo. Pe lânga asta, are rolul de a lega ciclul de viață al ApplicationContext-ului de ciclul de viață al ServletContext-ului și de a automatiza crearea ApplicationContext-ului, fără a mai fi nevoie ca programatorul să scrie cod explicit pentru realizarea acestui lucru.
Conexiunea cu baza de date se definește tot în fișierul applicationContext.xml. Există două moduri de definire, folosind clasa java.sql.DriverManager sau interfața javax.sql.DataSource. Voi prezenta a doua opțiune, aceasta fiind cea foosită de mine în proiect.
Spring folosește interfața DataSource pentru a obține o conexiune internă cu baza de date, cu implementarea org.springframework.jdbc.datasource.DriverManagerDataSource. Prin aceasta, pentru fiecare operație ce interacționează cu baza de date, framework-ul Spring deschide câte o conexiune nouă și o închide la final. În figura 33 este prezentată configurarea necesară conexiunii cu baza de date MySQL.
Fig. 33. Configurarea conexiunii cu baza de date
Pentru configurare este necesară specificarea a patru proprietăți: numele driverului folosit pentru conexiune, URL-ul bazei de date, numele de utilizator și parola de acces la baza de date. Acestea pot fi observate în exemplul de mai sus în declararea beanului cu id-ul „dataSource”. Am prezentat și o a doua variantă de configurare, folosită în general în mediile companiilor, unde informațiile despre baza de date pot fi securizate și vizibile doar anumitor utilizatori.
În acest caz se va folosi un PropertyPlaceholderConfigurer, prin care datele despre conexiune vor fi accesate dintr-un fișier separat de proprietăți, a cărui locație este indicată de proprietatea location, specificată în beanul cu id-ul „propertyConfigurer”. În aplicația mea am creat un fișier de proprietăți, numit database.properties, în care am introdus aceleași informații despre conexiune ca și în varianta „dataSource”, specificând și un nume de variabilă pentru fiecare proprietate, nume care va putea fi folosit ulterior pentru accesarea respectivei proprietăți, în formatul ${nume_proprietate}.
Interacțiunea cu baza de date în aplicație se face folosind frameworkul Hibernate JPA, mai exact folosind implementarea Spring a interfeței EntityManager, care este pusă la dispoziție prin implementarea interfeței EntityManagerFactory. Așadar, procesul de configurare a Hibernate JPA în Spring constă de fapt în declararea bean-ului Spring care va furniza clasa EntityManagerFactory, așa cum este ilustrat in figura 34.
Fig. 34. Configurarea bean-ului EntityManager
Există două tipuri de EntityManagers, și anume Application Managed și Container Managed, diferența între ele constând în modul în care sunt create. Al doilea tip, cel folosit de mine în proiect, implementează interfața EntityManager și este creat de un container Java EE, care face disponibil EntityManager-ul în codul aplicației prin injecția de dependențe sau JNDI (Java Naming and Directory Interface). Am ales această variantă deoarece aplicația nu mai este interesată de administrarea EntityManager-ului dupa crearea lui, lăsând sarcina pe seama containerului care îl furnizează.
Configurarea bean-ului pentru Container Managed Entity Manager trebuie să specifice anumite opțiuni:
sursa de date (DataSource)
implementarea de JPA ce va fi folosită, în cazul de față HibernateJpaVendorAdapter
un PersistenceUnitManager, prin care se indică ce entități vor fi utilizate în aplicație
3.2.3 persistence.xml
După cum am menționat și mai sus, în capitolul dedicat tehnologiilor folosite, Hibernate este un framework ORM, ceea ce înseamnă că face legătura între domeniul programării orientate obiect și domeniul relațional al bazelor de date. Acesta facilitează interacțiunea programatorului OOP cu baza de date, astfel încât să pară ca dezvoltatorul se află tot în domeniul OOP. Lucrul cu acest utilitar necesită existența claselor entități, care nu sunt altceva decât reprezentări ale tabelelor din baza de date sub formă de clase Java, și ale caror instanțe pot fi salvate în baza de date.
Un PersistenceUnit în Hibernate JPA este o grupare logică a una sau mai multe entități, împreună cu alte configurări suplimentare necesare pentru a lucra cu entitățile, cum ar fi sursa de date sau fișierul de mapare ORM, în cazul în care se folosește maparea bazată pe XML.
Configurarea unui PersistenceUnitManager necesită existența unui fișier persistence.xml, care se găsește in folderul META-INF din classpath.
Figura 35 prezintă fișierul persistence.xml al aplicației mele, in care se poate observa declararea bean-ului „persistenceUnit” prin intermediul tagului <persistence-unit>, specificarea claselor entități din aplicație, prin eticheta <class> și a proprietăților necesare conexiunii la baza de date.
Fig. 35. Fișierul persistence.xml
3.2.4 security-config.xml
Acest fișier este necesar pentru configurarea securității aplicației, folosind Spring Security, componentă a frameworkului Spring. În figura 36 este ilustrat modul în care se realizează securitatea în proiect: toate paginile din directorul /pages/secure/user vor putea fi accesate doar dacă utilizatorul are un cont valid și este logat, iar paginile din directorul /pages/secure/admin vor putea fi vizibile doar dacă utilizatorul are un cont de administrator valid și este logat cu acesta. Toate celelalte resurse ale aplicației vor fi accesibile și vizitatorilor (utilizatori care nu au un cont și nu sunt logați).
Elementul <http> este părintele tuturor funcționalităților namespace-urilor legate de web. Elementele <intercept-url> definesc tipare cu care se compară URL-urile cererilor. Atributele access definesc cerințele de acces ce trebuie îndeplinite pentru cererile ce se potrivesc tiparelor asociate. Se folosesc mai multe elemente <intercept-url> în cazul în care există cerințe diferite de acces pentru URL-uri diferite, acestea fiind evaluate în ordinea în care sunt listate în fișierul de configurare. Se va aplica prima regulă din listă care se potrivește, așa că cele mai specifice URL-uri trebuie declarate primele.
Fig. 36. Fișierul de configurare a securității aplicației
Folosirea elementului <authentication-provider> înseamnă că informațiile despre utilizator vor fi necesare <authentication-manager-ului> pentru a procesa cererile de autentificare. Acesta din urmă creaza un ProviderManager prin intermediul căruia înregistrează <authentication-providerii> Pot exista mai multe elemente <authentication-provider>, fiecare definind surse diferite de autentificare. Aceste elemente crează la rândul lor bean-uri DaoAuthenticationProvider.
Elementul <password-encoder> oferă suport pentru criptarea și decriptarea parolelor folosind un algoritm de hashing. În proiect, această funcționalitate este realizată de BcryptPasswordEncoder, o implementare a interfeței PasswordEncoder oferită de Spring.
3.2.5 mail-config.xml
Soluția standard pentru trimiterea de e-mailuri în Java este API-ul JavaMail. După cum este specificat și în pagina oficială, API-ul JavaMail pune la dispoziție un framework independent de platformă și de protocol, pentru a dezvolta aplicații ce trimit e-mailuri și mesaje.
Din păcate, soluția JavaMail este greoaie și dificil de configurat, o soluție mult mai facilă fiind abstractizarea trimiterii de e-mailuri realizată de frameworkul Spring. Spring pune la dispoziție o bibliotecă utilă pentru trimiterea de e-mailuri, care izolează utilizatorul de specificul ce stă la baza sistemului de mailing și realizează manipularea resurselor în locul clientului.
În proiect am folosit interfața MailSender oferită de Spring, cu implementarea JavaMailSenderImpl, care suporta atât trimiterea de mesaje simple, cât și a celor cu atașamente. Configurarea acesteia se realizează în fișierul mail-config.xml.
3.2.6 faces-config.xml
Pentru integrarea frameworkului Spring într-o aplicație JSF am preferat configurarea prin adnotări Spring și menținerea posibilității de manipulare a bean-urilor JSF din front end. Pentru a folosi clasele administrate de Spring ca bean-uri JSF, este necesară setarea unui el-resolver in fișierul de configurare JSF, ce poartă numele de faces-config.xml, ca în figura de mai jos.
Fig. 37. Declararea unui SpringBeanFacesELResolver ăn fișierul de configurare JSF
Acesta oferă posibilitatea de a configura o clasă Spring ca bean administrat de JSF folosind doar adnotări.
Fig. 38. Configurarea unei clase Spring ca bean administrat de JSF, folosind adnotări
4. Build și Deployment
După implementarea propriu zisă a aplicației, după scrierea codului, proiectul trece prin faza de compilare a surselor, împachetarea acestora și instalarea lor pe un server, de unde pot fi acccesate de utilizatori. Ansamblul acestor activități poartă numele de build. Pot exista în procesul de build mai multe activități decât cele menționate.
În proiect am ales să folosesc un utilitar de automatizare a build-ului, care realizează toate aceste activități fără intervenția programatorului, deoarece în acest mod se diminuează riscul apariției erorilor și, de asemenea, timpul executării tuturor fazelor build-ului de către un utilitar automat este mult mai redus decât timpul necesar dezvoltatorului să realizeze aceleași activități.
Utilitarul ales este Maven, un tool centrat în jurul conceptului de fișiere POM (Project Object Model). Fișierul POM este reprezentarea XML a resurselor proiectului, precum cod sursă, codul pentru teste, dependențe (JAR-uri externe folosite) și altele. Acest fișier conține referințe către toate aceste resurse și trebuie să se găsească in directorul rădăcină al proiectului.
Mai jos este o diagramă a modului în care Maven folosește fișierul POM și ce conține acesta.
Fig. 39. Modul de funcționare al utilitarului Maven[13]
La executarea unei comenzi Maven, utilitarul are nevoie de un fișier POM pe baza căruia să execute respectiva comandă, descarcă dependențele în repository-ul local, execută ciclurile de viață, fazele și goal-urile din fișierul POM și apoi plugin-urile, toate în concordanță cu profilul de build ales.
După cum se poate observa, resursele fișierului POM includ ciclurile de viață ale build-ului, faze, goal-uri, dependențe, plugin-uri și profile pentru build.
Procesul de build în Maven se împarte în cicluri de viață, faze și goal-uri (ținte, obiective). Un ciclu de viață al unui build conține o secvență de faze, iar fiecare fază este formată dintr-o secvență de goal-uri. La rularea Maven se specifică o comandă care va reprezenta de fapt numele unui ciclu de viață, al unei faze sau al unui goal.
Unul din primele goal-uri executate de Maven este verificarea dependențelor necesare proiectului. Dependențele sunt JAR-uri externe (biblioteci Java) folosite în proiect. Dacă acestea nu se află în repository-ul local Maven, utilitarul le va descărca de pe un repository central și le va adăuga în cel local. Repository-ul local este, de fapt, un folder de pe hard disk-ul calculatorului, având o locație implicită, dar putând fi și specificată explicit.
Plugin-urile build-ului sunt utilizate pentru a adăuga goal-uri suplimentare într-o fază a build-ului. Necesitatea lor apare dacă este nevoie de executarea unui set de activități ce nu sunt specificate de standardul Maven, caz în care se poate adăuga un plugin în fișierul POM. Există în Maven atât plugin-uri standard cât și posibilitatea scrierii propriilor plugin-uri în Java.
Profilele sunt folosite atunci când e necesar să se facă build-ul proiectului în mai multe moduri. De exemplu, e nevoie de un build pentru sistemul local, unul pentru dezvoltare și unul pentru testare. La executrea Maven se va specifica ce profil să se folosească pentru build.
Rularea Maven se realizează prin executarea comenzii mvn într-o consolă, urmată de specificarea unui ciclu de viață ce se dorește executat, a unei faze sau a unui goal.
Cel mai important ciclu de viață în Maven este cel default, deoarece este cel care realizează build-ul codului. Pentru a executa acest ciclu, nu i se poate pasa numele ca argument al comenzii mvn, ci trebuie specificate, pe rând, fazele sale. În tabelul de mai jos sunt prezentate fazele ciclului de viață implicit, împreună cu o scurtă descriere a fiecăreia.
Tabelul 1
Fazele ciclului de viață default și descrierea fiecăreia
Pentru ușurința distribuirii aplicației pe un server (mai exact ultima fază descrisă mai sus), este de preferat ca toate resursele proiectului să fie împachetate într-o arhivă web (sau fișier WAR), în care să fie incluse toate clasele, imaginile, bibliotecile de etichete, fișierele jsp, servlet-urile și alte resurse necesare funcționării aplicației.
Astfel, atunci când se face deploy-ul, se va copia pe server un singur fișier, cu extensia .war, și nu o întreagă ierarhie de directoare și fișiere, cum este de fapt reprezentat proiectul.
După faza de deploy, aplicația este disponibilă utilizatorilor și poate fi accesată de aceștia.
5. Studiu de caz
După ce aplicația a fost lansată pe server, aceasta poate fi accesată de utilizatori prin intermediului unui browser web. Pentru o mai bună înțelegere a procesului prin care se poate utiliza aplicația, diagrama de mai jos descrie pașii urmați de un utilizator pentru a adăuga un curs, de la accesarea paginii principale, și până la vizualizarea la final a cursului adăugat.
Fig. 40. Organigrama procesului de adăugare a unui curs în aplicație
Așadar, urmărind diagrama de mai sus, la intrarea în aplicație, se verifică dacă utilizatorul este autentificat. În caz afirmativ, acesta va naviga la pagina de adăugare cursuri, apăsând butonul Contribuie, din colțul dreapta sus al paginii. Altfel, va trebui să se autentifice. Dacă nu are un cont în aplicație, va trebui să își creeze unul, din pagina de înregistrare, după care se poate loga în site și va accesa pagina de adăugare de noi cursuri.
Fig. 41. Navigarea la pagina de adăugare cursuri de către un utilizator autentificat
Fig. 42. Navigarea la pagina de înregistrare, din pagina de login, pentru un utilizator neînregistrat
Pentru a putea adăuga un curs, va trebui să selecteze categoria și subcategoria din care acesta face parte. Dacă nu există încă în aplicație o categorie, respectiv o subcategorie corespunzătoare, utilizatorul va putea crea unele noi, prin apăsarea butoanelor Add new category, și Add new subcategory.
Fig. 43. Selectarea categoriei/subcategoriei
Fig. 44. Adăugare a unei noi categorii
După acest pas, poate selecta tipul de curs ce va fi încărcat: curs în format text, pdf sau tutorial video. În cazul ultimei variante, se va afișa un tabel în care se pot introduce lecții, fiecare lecție având un titlu și un conținut video. În cazul celorlalte două variante, se va adăuga conținutul cursului fie prin introducere de text într-un editor, fie prin uploadarea fișierului pdf.
Fig. 45. Selectarea tipului de curs tutorial video
Fig 46. Selectarea tipului de curs text (stânga) și fișier pdf (dreapta)
După completarea tuturror detaliilor despre curs, utilizatorul îl poate salva, apăsând butonul Save, după care va fi informat de salvarea cu succes, sau în cazul unei erori apărute, de imposibilitatea salvării cursului.
Fig. 47. Salvarea cursului eșuată
Fig. 48. Salvarea cu succes a cursului
Pentru a-l vizualiza, acesta poate naviga la pagina corespunzătoare, prin meniul din partea stângă, de unde va alege categoria și subcategoria din care face parte cursul adăugat.
Fig. 49. Navigarea la cursul salvat, din meniul lateral stânga
6. Dezvoltări viitoare
Aplicația a fost gândită și construită modular, astfel încât vor fi posibile dezvoltări viitoare fără modificări majore ale codului existent.
Există o multitudine de moduri prin care aplicația ar putea fi extinsă pe viitor, una din cele mai importante, dupa părerea mea, fiind adăugarea unui modul prin care utilizatori își pot verifica cunoștințele prin teste online. Prin urmare, cei ce adaugă cursurile, ar avea la dispoziție și posibilitatea să creeze teste pe baza lor. Fiecare utilizator va putea astfel, după urmarea cursului, să suțină testele aferente acestuia și în acest mod să afle gradul de înțelegere la care a ajuns după parcurgerea lui. Iar pentru a-și putea urmări progresul, rezultatele obținute în urma susținerii testelor, ar putea fi afișate într-o pagină de profil a fiecarui utilizator.
Un al posibil mod de extindere ar putea fi punerea la dispoziție a unei modalități de comunicare între utilizatori. Este demonstrat faptul că oamenii învață în general mai ușor atunci când fac parte dintr-un grup, sunt mai motivați, deoarece pot căpăta o înțelegere completă a ceea ce studiază, prin compararea cu ceilalți, iar discuțiile pe tematici de studiu promovează gândirea în asamblu, din mai multe perspective. De aceea, consider că o bună modalitate de a susține învățarea ar fi integrarea aplicației cu un modul care să permită interacțiunea între utilizatorii ce urmează aceleași cursuri, și de ce nu, chiar cu profilele acestora de pe rețelele de socializare. În acest mod, utilizatori pot pune întrebări, pot purta discuții referitoare la cursuri, obținând astfel o înțelegere cât mai completă a informațiilor puse la dispoziție în aplicație.
7. Concluzii
În concluzie, prin dezvoltarea acestui proiect am realizat o aplicație cu scop educativ, destinată acelor utilizatori care vor să obțină informații din domenii diverse de interes, într-un mod rapid, sau celor care vor să împărtășască cu ceilalți cunoștințele lor, care vor să contribuie prin încărcarea de cursuri, la dezvoltarea aplicației.
Am construit această aplicație astfel încât să permită structurarea informațiilor într-un mod intuitiv, ierarhic, tocmai pentru a o face ușor de utilizat, un factor important în ușurința utilizării fiind și interfața grafică. Nu există noțiunea de student și profesor, oricine dorește să contribuie având posibilitatea să o facă, singura condiție necesară fiind crearea unui cont de utilizator. Totuși, pentru a păstra caracterul educativ și informativ al aplicației, vor exista utilizatori cu rol de administratori, care vor putea filtra conținutul irelevant încărcat în site și vor fi aleși pe baza cunoștințelor deținute în diversele domenii de studiu existente în aplicație.
Pentru a oferi o experiență cât mai plăcută utilizatorilor, pentru a le pune la dispoziție toate măsurile necesare astfel încât procesul de învățare să decurgă ușor și rapid, și pentru ca aceștia să nu întâlnească dificultăți în înțelegerea conținutului, există în aplicație posibilitatea adăugării de cursuri în mai multe formate: text, pdf sau video. De asemenea, am implementat în aplicație un sistem de votare a cursurilor de către utilizatorii autentificați, astfel încât aceșia să poată oferi feedback despre cursurile pe care le urmează.
Datorită tehnologiei, în prezent asistăm la o transformare radicală a modului în care se face educație, aceasta nemafiind centrată, așa cum era în trecut, pe profesori și pe modul de predare al acestora, ci pe cei cei ce învață si pe procesul de învățare. Prin funcționalitățile pe care le oferă, aplicația prezentată în această lucrare răspunde nevoii de învățare a utilizatorilor, în concordanță cu situația actuală.
bibliography
[1] M. Hamada, „E-Learning: New Technology, Applications and Future Trends”, Nova Science Pub Inc, 2013
[2] http://elearninginfographics.com/past-present-future-online-education-infographic/
[3] https://ist.berkeley.edu/as-ag/pub/pdf/mvc-seminar.pdf, Berkley University seminar
[4] „An introduction to creating JSF applications in Rational Application Developer Version 8.0”, IBM Corporation, 2010
[5] R. Johnson, J. Hoeller, K. Donald, C. Sampaleanu, R. Harrop, „Spring Framework Reference Documentation”
[6] V. Iyer, E. Hanes Perry, B. Wright, T. Pfaeffle, „JDBC Developer’s Guide and Reference”, 2010
[7] G. King, C. Bauer, M. R. Andersen, E. Bernard, S. Ebersole, „HIBERNATE – Relational Persistence for Idiomatic Java”
[8] K. K. Panigrahi, A. Raj, M. Kumar, S. Yunus, M. Shejwal, S. Kallakuri, „Java Server Faces (JSF) Tutorial”
[9] Sing Li, „Tomcat for beginning Web developers”, Wrox Press, 2005
[10] B. Kurniawan, P. Deck, „How Tomcat Works: A Guide to Developing Your Own Java Servlet Container”, Boyd Printing Company Inc, 2004
[11] http://www.datadisk.co.uk/html_docs/java_app/tomcat6/tomcat6_architecture.html
[12] B. Wright, T. Smith, „Oracle Application Server Containers for J2EE – Servlet Developer’s Guide”, Oracle, 2005
[13] http://tutorials.jenkov.com/maven/maven-tutorial.html
Copyright Notice
© Licențiada.org respectă drepturile de proprietate intelectuală și așteaptă ca toți utilizatorii să facă același lucru. Dacă consideri că un conținut de pe site încalcă drepturile tale de autor, te rugăm să trimiți o notificare DMCA.
Acest articol: Aplicatie Web Pentru Structurarea Informatiilor Intr O Platforma Educationala (ID: 109954)
Dacă considerați că acest conținut vă încalcă drepturile de autor, vă rugăm să depuneți o cerere pe pagina noastră Copyright Takedown.
