Crearea Unei Aplicatii In Doemniul Medical
Cuprins
Capitolul 1
INTRODUCERE
Contextul proiectului
Nu este foarte dificil să realizăm faptul că trăim într-o lume dominată de tehnologie. Tehnologia mobilă din ultimii ani a început să îmbine utilul cu plăcutul. Ușor ne putem da seama că orice tip de tehnologie, nu doar cea mobilă crește gradul de confort. În ultimul deceniu au apărut din ce în ce mai multe rețelele de socializare.
Rețelele de socializare reprezintă în prezent o țintă în continuă mișcare pentru mulți dintre cercetatori. Printre rețelele de socializare cunoscute putem să amintim: hi5, Netlog, Facebook, Twitter, Linkedin și exemplele pot continua. Desigur, rețelele de socializare dețin o serie de avantaje, ca de exemplu strânsa relație și comunicare dintre utilizatori, precum și anumite avantaje privind folosirea unor platforme online pentru dobândirea de noi cunoștințe. Rețelele de socializare sunt folosite de utilizatori pentru a menține o conexiune continuă între aceștia, pentru a-și face noi prieteni, pentru a vizualiza sau a posta noi fotografii sau chiar pentru întâlniri. Printre rețelele de socializare normale mai există si rețelele de socializare ad-hoc.
Rețeaua de socializare ad-hoc(ASN) reprezintă o combinație între rețeaua socială si rețeaua ad-hoc. În ziua de azi, majoritatea telefoanelor mobile sunt echipate cu Bluetooth, Wi-Fi precum si rețele celulare de date. Deoarece conțin astfel de echipamente acestea sunt capabile să suporte comunicarea ad-hoc.
Rețeaua de socializare ad-hoc este o rețea de socializare unde persoane cu interese comune se conectează folosind comunicarea ad-hoc a telefoanelor mobile. Printre principalele avantaje ale rețelei de socializare ad-hoc amintim infrastructura low-level folosită pentru partea de comunicare. ASN este cu atât mai semnificativă pentru utilizatorii care doresc să găsească alți utilizatori având interese comune și care se află în aceeași rază de acoperire. În acest fel, aceștia pot comunica prin chat sau chiar socializa față-n față. Rețeaua de socializare ad-hoc a fost propusă întotdeauna din cauza faptului că nu necesită conexiune la internet.
Tema lucrării
Principalul scop al aplicației este menținerea conexiunii dintre utilizatorii cu interese comune, fără folosirea prea multor resurse cum ar fi conexiunea la internet.
Pentru indeplinirea acestui scop, sistemul va pune la dispoziție utilizatorilor o aplicație compatibilă sistemului de operare Android. În momentul în care un utilizator deschide aplicația și se conectează la un Access Point, acesta va fi introdus automat ca și utilizator în aplicație. Utilizatorul va fi activ în aplicație cât timp cât va fi menținută o conexiune activă la acel Access Point. Odată ce nu mai există o conexiune activă la Access Point, serverul va demara ștergerea imediată a utilizatorului respectiv.
Cu toții știm că majoritatea rețelelor de socializare existente necesită conexiune la internet. ASN reprezintă o soluție viabilă pentru aplicațiile care transmit la orice moment, oriunde informații de natură socială precum utilizarea unui taxi in aeroport pentru o anumită destinație, prezentarea unor promoții ale anumitor magazine dintr-un mall. Sunt momente în care suntem într-un mall și nu știm că anumite magazine oferă reduceri în acea zi, sau că haina noastră preferată pe care am văzut-o online pe site-ul unui magazin se află și in reprezentanță. Ei bine, toate aceste nevoi pot fi rezolvate cu ajutorul unei rețele de socializare unde utilizatorii cu aceleași interese pot comunica
Pentru utilizarea acestui sistem, condiția necesară este deținerea unor dispozitive care să aibă rolul de gazdă pentru aplicațiile sistemului: pentru partea de server este necesar un calculator care să ruleze o mașină virtuală de Linux sau Linux nativ și sa fie conectat la același Access Point precum utilizatorii. Fiecare utilizator va avea nevoie de un smartphone cu sistem de operare Android având versiune minimă API 17.
Capitolul 2
OBIECTIVELE SI SPECIFICAȚIILE PROIECTULUI
2.1 Obiectivele proiectului
Scopul acestei aplicații este menținerea unei conexiuni cât mai strânse între utilizatorii cu interese comune, fără folosirea prea multor resurse cum ar fi conexiunea la internet.
Principalele funcționalități implementate sunt următoarele :
Logare în aplicație folosind broadcasting
Modificarea informațiilor utilizatorului curent
Adăugarea unei activități
Ștergerea unei activități a utilizatorului curent
Adăugarea unui comentariu la o activitate
Ștergerea unui comentariu al utilizatorului curent
Vizualizarea tuturor comentariilor unei activități
Distribuirea unei activități pe peretele utilizatorului curent
Efectuarea rating-ului unei activități
Vizualizare listă cu utilizatorii care au efectuat rating
Vizualizare listă de prieteni
Vizualizarea profilului unui prieten
Realizarea unui chat instant între doi utilizatori
2.1.1 Specificații funcționale
2.1.1.1 Logare în aplicație folosind broadcasting
Mecanismul de logare se va realiza în felul următor: utilizatorul se va conecta la un Access Point folosind device-ul Android. Odată ce va deschide aplicația, device-ul Android va trimite un broadcast tuturor device-urilor conectate la acel Access Point. Dacă există o aplicație server în interiorul acelei rețele, IP-ul device-ului care a trimis broadcast-ul va fi automat salvat ca și utilizator în aplicație. Pe măsură ce utilizatorul este logat în aplicație, aplicația client va trimite un "heartbeat" la fiecare 5 secunde pentru autentificare. Dacă nu primește răspuns de la server, acesta este automat delogat iar toate informațiile acestuia vor fi șterse.
2.1.1.2 Modificarea informațiilor utilizatorului curent
La prima logare în aplicație, utilizatorul va avea un nume predefinit, "Default First name Default Last name", respectiv două fotografii predefinite: una de profil și una de background. Utilizatorul are posibilitatea să modifice atât informațiile personale cât și fotografiile aferente. Modificarea unei fotografii poate fi realizată în două moduri: prin alegerea unei noi fotografii din galeria telefonului, respectiv prin captarea unei noi fotografii folosind camera smartphone-ului.
2.1.1.3 Adăugarea unei activități
Utilizatorul are posibilitatea de adăugare a unei noi activități. Activitatea creată va fi vizibilă în interiorul ferestrei Profile. Restul utilizatorilor vor putea vizualiza activitatea în interiorul ferestrei News Feed.
Activitatea poate să conțină o scurtă descriere sau o fotografie. Adăugarea fotografiei se poate realiza în două moduri: prin alegerea unei noi fotografii din galeria telefonului, respectiv prin captarea unei noi fotografii folosind camera smartphone-ului.
2.1.1.4 Ștergerea unei activități a utilizatorului curent
Utilizatorul are posibilitatea de ștergere a unei activități creată de acesta. Aceasta poate fi ștearsă din fereastra Profile.
2.1.1.5 Adăugarea unui comentariu la o activitate
Utilizatorul are posibilitatea de a adăuga comentarii, atât la activitățiile create de prietenii săi cât și la propriile activități.
2.1.1.6 Ștergerea unui comentariu al utilizatorului curent
Utilizatorul are posibilitatea ștergerii unui comentariu adăugat la o activitate.
2.1.1.7 Vizualizarea tuturor comentariilor unei activități
Fiecare utilizator are posibilitatea vizualizării tuturor comentariilor aferente unei activități, indiferent dacă aceasta este creată de el sau de un alt utilizator.
2.1.1.8 Distribuirea unei activități pe peretele utilizatorului curent
Utilizatorul are posibilitatea distribuirii unei activități de pe profilul unui prieten sau din fereastra News Feed. Ulterior, această activitate va fi vizibilă în fereastra Profile.
2.1.1.9 Efectuarea rating-ului unei activități
Fiecare activitate conține în alcătuirea acesteia un rating general. Utilizatorul are posibilitatea de a efectua rating unei activități. Valoarea minimă disponibilă a rating-ului este 0.5 iar valoarea maximă este 5. Ratingul general se calculează efectuându-se media aritmetică a tuturor rating-urilor. Efectuarea rating-ului unei activități se poate face o singură dată de către utilizator, efectuarea ulterioară fiind indisponibilă.
2.2.1.10 Vizualizare listă cu utilizatorii care au efectuat rating
Pentru fiecare activitate existentă, utilizatorul are posibilitatea vizualizării unei liste cu toți utilizatorii care au efectuat rating. Pentru fiecare utilizator vor fi disponibile informații legate de nume, fotografia curentă de profil, valoarea rating-ului respectiv data când acest rating a fost efectuat.
2.1.1.11 Vizualizare listă de prieteni
Odată logat în aplicație, utilizatorul are posiblitatea vizualizării unei liste cu utilizatorii existenți în aplicație. Această listă va fi modificată de fiecare dată când un utilizator este adăugat sau părăsește aplicația. Pentru fiecare utilizator vor fi disponibile informații legate de nume, fotografia curentă de profil respectiv data logării în interiorul aplicației.
2.2.1.12 Vizualizarea profilului unui prieten
Utilizatorul are posibilitatea vizualizării profilului oricărui alt utilizator existent în aplicație. Acest lucru se poate realiza din fereastra Friends respectiv News Feed accesând fotografia de profil a utilizatorului respectiv. Pe profilul acestuia se pot viziona informații despre acesta precum și toate activitățile postate.
2.2.1.13 Realizarea unui chat instant între doi utilizatori
Aplicația permite trimiterea de mesaje în mod instant între doi utilizatori. La logarea utilizatorului în aplicație, acesta este automat adăugat într-un grup default pe serverul de chat, având automat statusul "Available". Accesul la lista de prieteni activi pe chat se realizează prin intermediul ferestrei "Chat".
2.2.2 Specificații non-funcționale
2.2.2.1 Interfața prietenoasă
Fiind o aplicație destinată interacțiunii dintre persoanele cu interese comune, aceasta trebuie să fie ușor de utilizat, să aibă o interfață prietenoasă(engl."user-friendly") și intuitivă astfel încât să ofere utilizatorilor o experiență cât mai placută.
2.2.2.2 Utilizabilitate
Sistemul permite o utilizare intuitivă , cu o interfața grafică prietenoasă astfel încât utilizarea acestuia să devină o plăcere pentru utilizatorii acesteia, realizând într-un mod cât mai plăcut interacțiunea dintre utilizatori.
2.2.2.3 Securitatea
Securitatea este un aspect important al sistemului, aceasta permițând doar unui utilizator autorizat(admin) să adauge utilizatorii în interiorul aplicației. Fiecare request realizat de către aplicația mobilă este securizat folosind un token unic pentru fiecare utilizator. Acest token se generează la conectarea utilizatorului la aplicație și este activ până la delogarea acestuia.
2.2.2.4 Performanța
Performanțele sistemului fac referire la timpul de răspuns al acestuia, executarea interogărilor și afișarea rezultatelor. Timpul de răspuns trebuie să fie optim indiferent de numărul utilizatorilor care accesează în același timp aplicația, numărul de interogări sau de complexitatea și dimensiunea bazei de date (care în timp va crește considerabil). De asemenea, executarea interogărilor și afișarea rezultatelor trebuie să aibă un timp optim de răspuns, astfel încât activitatea utilizatorului să nu fie întreruptă de blocarea sistemului sau de așteptarea unor informații din partea acestuia. De aceea, majoritatea request-urilor care necesită returnarea unor rezultate de dimensiuni mari folosesc noțiunea de paginare, numărul maxim default de rezultate returnate fiind 10.
2.2.2.5 Scalabilitatea
Scalabilitatea sistemului reprezintă capacitatea acestuia de a suporta un volum mare de încărcare sau de a permite extinderea sa. Despre un sistem de stocare și prelucrare a datelor se poate spune ca este scalabil daca se comportă similar chiar dacă volumul de date pe care îl
prelucrează devine tot mai mare. Un motiv în plus pentru folosirea unei baze de date NoSQL reprezintă faptul că aceasta are abilitatea de a scala orizontal pe mai multe servere, precum și abilitatea de a replica și distribui datele pe mai multe servere.
2.2.2.6 Robustețe
Aplicația trebuie să nu fie vulnerabilă la erori și date invalide introduse de utilizatori. În cazul datelor invalide, aplicația trebuie sa se comporte rezonabil, cea mai binevenită ar fi afișarea unui mesaj care să informeze utilizatorul despre datele introduse greșit.
Capitolul 3
Studiu bibliografic
Concepte de bază
3.1.1 Arhitectura REST
Serviciul REST(Representational State Transfer) reprezintă un stil software arhitectural pentru sisteme hypermedia distribuite, cum ar fi World Wide Web. În cadrul serviciului REST, nu se pune accent pe detaliile legate de implementare ci mai degrabă pe rolul fiecărei componente și pe un set de interacțiuni dintre aceste componente. [30]
Conform [30], scopul acestui serviciu este de a crește performanta, scalabilitatea, simplicitatea, vizibilitatea respectiv portabilitatea sistemelor distribuite. Într-un context mai puțin riguros, serviciul descrie o interfață care transmite date prin HTTP, evitând folosirea sesiunilor prin cookie-uri HTTP.
În cadrul acestui serviciu, transmiterea datelor nu este restransă doar la HTTP. Se pot dezvolta aplicații software care să respecte principiile REST dar fără să folosească HTTP si fără să interacționeze prin intermediul World Wide Web. Printre pricipalele exemple de implementare, putem enumera transmiterea datelor prin fișiere XML sau prin structuri de date precum JSON(JavaScript Object Notation).
Conceptul de serviciu REST(Representational State Transfer) a fost introdus și definit de către Roy Fielding în teza acestuia de doctorat la Universitatea Ivine din California în anul 2000. În cadrul serviciilor REST, totul este o resursă. Orice poate fi stocat în calculator sub formă de șir de biți poate fi considerat o resursă. Posibile resurse pot fi:
o listă de utilizatori
numărul de cumpărători al unui produs
o nouă activitate pe o rețea de socializare
Conform [31], în cele mai multe cazuri, metodele folosite pentru realizarea serviciilor RESTful sunt:
POST – crearea unei noi resurse. (adesea poate fi folosită și la modificarea unei resurse existente). Execuția repetată a acestei metode poate avea efecte distince.
PUT – modificarea unei resurse existente. Execuția repetată a acestei metode va avea exact același efect ca și modificarea singulară.
GET – citirea unei resurse fără modificarea acesteia. Execuția repetată a acestei metode va avea exact același efect ca și execuția singulară.
DELETE – ștergerea unei resurse existente. Execuția repetată a acestei metode va avea același efect ca și o singură execuție. IDEMPOTENT.
Conform [31], putem enumera câteva aspecte legate de arhitectura REST:
este un stil arhitectural simplu bazat pe standarde Web și HTTP
este o arhitectură client-server bazată pe resurse
Resursele unei aplicații au adrese de forma
http://{server}:{port}/users/1
spre deosebire de arhitectura SOAP a cărei reprezentare a mesajelor se face doar prin XML, resursele în cazul arhitecturii REST pot avea diferite reprezentări precum XML, JSON, TEXT, OCTET-STREAM, JPEG, folosirea acestora determinându-se conform protocolului HTTP(de exemplu HTTP Headers). Un client poate cere un anumit format al răspunsului prin protocolul HTTP.(Content Negotiation)
tratarea erorilor se realizează conform tratării erorilor în protocolul HTTP
este o arhitectură scalabilă datorită separării responsabilităților între client și server. De exemplu, serverul este responsabil de managementul datelor, acesta nemenținând starea unui utilizator între request-uri. Responsabilitatea clientului este de a menține această stare.
clienții pot folosi tehnici de caching pentru creșterea performanței și scalabilității unei soluții
Implementările de servicii Web REST s-au modificat rapid în ultimii ani, influențate fiind mai ales de apariția adnotărilor Java și a standardului JAX-RS. JAX-RS este un API destinat să ușureze dezvoltarea aplicațiilor bazate pe arhitectura REST.
Exemple de adnotări:
@Path – reprezintă un URI relativ pentru o resursă. URI-ul este static, dar putem să includem și variabile în acesta.
@GET – este destinată metodei cererii, împreună cu @POST, @PUT, @DELETE fiind definite de JAX-RS conform metodelor HTTP prezentate anterior.
@Produces – specifică tipul MIME media pe care o resursă îl poate reproduce și îl trimite clientului.
Exemplu: application/json, application/xml, application/octet-stream, image/*, text/plain.
@Consumes – specifică tipul MIME pe care o resursă îl poate consuma și a fost trimis de către client.
Exemplu: application/json, application/xml, application/octet-stream, text/plain.
@PathParam – ajuta la maparea anumitor fragmente din URI.
Exemplu: Să presupunem că avem URI-ul "/users/{userId}".
@PathParam("userId") String userId – va asigna valoarea mapată in URI variabilei userId, variabilă ce ulterior va putea fi folosită în metoda curentă.
Una dintre cele mai importante implementări JAX-RS este folosind framework-ul Jersey, framework despre care vom discuta în capitolul următor. Printre alte exemple de implementări putem enumera: Apache CFX, RESTEasy, Restlet, Apache Wink.
3.1.2 Arhitectura Three-Tier
Arhitectura 3-tier reprezintă un model de arhitectură client-server în care interfața cu utilizatorul(layer-ul de prezentare), logica funcțională(layer-ul de business) și layer-ul de date sunt dezvoltate și menținute ca și module independente, adesea sunt separate pe diferite platforme. Acest tip de arhitectură a fost dezvoltat de către John J. Donovan. [1]
Pe lângă modalitatea de programare modulară cu interfețe bine definite, arhitectura 3-tier intenționează să permită dezvoltătorului posibilitatea de upgrade al oricărui nivel din cele trei. De exemplu, dacă se dorește schimbarea modalității de stocare a informațiilor, de la o bază de date relațională la o bază de date nerelațională, singurul modul care va fi modificat va fi modulul de stocare al datelor, fără a influența și celelalte module.
Arhitectura 3-tier prezintă 3 mari module:
Nivelul de prezentare
Nivelul logic
Nivelul de stocare a datelor
Nivelul de prezentare(nivelul client) reprezintă nivelul superior al aplicației, afișând informații legate de anumite servicii cum ar fi căutarea și achiziționarea de mărfuri, gestionarea unui coș de cumpărături. [1] Acest nivel transmite informații și anumite rezultate spre client respectiv spre alte niveluri din interiorul aplicației. Comunică în mod exclusiv cu nivelul logic. [2]
Nivelul logic reprezintă puntea de legătura dintre nivelul de prezentare și nivelul de stocare al datelor. Pentru utilizator, nivelul logic reprezintă o vizualizare abstractă a bazei de date. [14] Fiind nivelul de mijloc, doar acesta are o conexiune directă cu nivelul de stocare a datelor,, pentru ceilalți clienți acest lucru fiind imposibil. La acest nivel se execută operații asupra datelor înainte de a fi transmise spre nivelul de stocare. Exemple de operații pot fi: operații de procesare a imaginilor, validarea unor informații înainte să fie introduse în baza de date.
Nivelul de stocare a datelor conține mecanisme de stocare ale datelor(servere de baze de date), respectiv accesul la date. Accesul la date încapsulează mecanismul de stocare și modul de expunere al acestora.[1] Nivelul de stocare al datelor trebuie să expună un API accesibil nivelului logic care să permită accesul la baza de date și executarea anumitor operații asupra bazei de date, dar fără să creeze dependențe în mecanismul de stocare a informațiilor. În acest fel, la o modificare a arhitecturii bazei de date, nivelul logic nu va fi afectat.
În figura următoare pot fi vizualizate cele 3 module, respectiv modalitatea de comunicare dintre acestea:
Figura 3.1.2.1 Arhitectura 3-tier [9]
3.1.3 Arhitectura Model-View-Controller(MVC)
Model-View-Controller este un model arhitectural utilizat în ingineria software pentru dezvoltarea aplicațiilor web. Este o arhitectură populară deoarece izolează logica aplicației de layer-ul de prezentare al aplicației. Arhitectura MVC este alcătuită din următoarele trei componente:
model
view
controller
Componenta Model este responsabilă cu reprezentarea datelor și a acțiunilor ce operează asupra datelor la nivelul Web al aplicației. Aceasta componentă răspunde reques-urilor primite de la componenta View precum și instrucțiunilor primite de la componenta Controller pentru modificarea propriilor informații. Conform [13], este reprezentat de baza de date al aplicației și de modelele care se ocupă cu adăugarea, ștergerea respectiv editarea datelor respective.
Componenta View se ocupă de afișarea datelor, de modul în care utilizatorului îi vor fi prezentate informațiile primite de la Controller. Această componentă este realizată astfel incât să genereze output-ul specific în funcție de necesitatea utilizatorului. Poate fi structurată în mai multe părți, fiecare având câte o funcție specifică, în funcție de formatul output-ului dorit de către utilizator.[13]
Componenta Controller reprezintă creierul arhitecturii. Controller-ul este punctul de legătură dintre componenta View și componenta Model. Primește informații de la utilizator prin intermediul componentei View, procesează aceste informații cu ajutorul componentei Model, iar în urma procesării returnează un răspuns componentei View.
În figura următoare pot fi vizualizate cele trei componente, precum și modalitatea de comunicare dintre acestea.
Figura 3.1.3.1 Arhitectura MVC [8]
Ca orice arhitectură, aceasta prezintă atât o serie de avantaje, cât și o serie de dezavantaje. Conform [28], putem enumera următoarele avantaje:
modularitate eficientă: modularitatea design-ului permite schimbarea oricărei componente, fără a aduce modificări majore asupra celorlalte.
folosirea cu ușurință a programării bazate pe teste (Test Driven Development -TDD)
ușurință în versionare: controller-ele și view-urile pot crește în paralel cu modelul.
prezintă extensibilitate, scalabilitate și facilitează reutilizarea codului.
permite mai multe reprezentări(views) ale aceleași informații(model): datorită separării dintre model și view, interfața cu utilizatorul poate afișa mai multe view-uri ale aceluiași model concomitent
Conform [4], putem enumera următoarele dezavantaje:
complexitatea: fiind o arhitectură complexă, este dificil de implementat
nu este indicată pentru aplicații de dimensiuni mici: necesită mult timp pentru construirea arhitecturii, transferarea informației între componente duce la creșterea complexității, deci implicit la creșterea performanței
Deși arhitecturile 3-tier și MVC conțin elemente similare, din punct de vedere topologic aceste două arhitecturi sunt diferite. Una dintre cele mai importante reguli cu privire la arhitectura 3-tier este faptul că nivelul de prezentare(client) nu comunică în mod direct cu nivelul de stocare a datelor. În cadrul arhitecturii 3-tier, toate request-urile trebuie să treacă prin nivelul logic, deci putem afirma faptul că aceasta este liniară. Nu putem spune același lucru și despre arhitectura MVC. În cadrul acestei arhitecturi, componenta View modifică componenta Controller care la rândul ei modifică componenta Model, iar componenta View este direct modificată de către componenta Model. Cele două arhitecturi pot fi folosite împreună în cadrul aceleași aplicații. În cadrul aplicației realizate, pentru componenta back-end (server) s-a folosit arhitectura 3-tier iar pentru componenta front-end(Android) s-a folosit arhitectura MVC, deoarece aceasta poate fi folosită în nivelul de prezentare al arhitecturii 3-tier.
Capitolul 4
Analiză și fundamentare teoretică
4.1 Docker
4.1.1 Arhitectura Docker
Potrivit [15], Docker-ul este o platformă folosită pentru dezvoltarea, livrarea și rularea aplicațiilor. Scopul acestei platforme este de a livra, testa și lansa rapid aplicația, scurtând acel ciclu între scrierea codului aplicației și rularea acestuia. Folosind docker, există o separare a aplicației față de infrastructura pe care aplicația va rula.
Docker-ul funcționează pe un sistem bazat pe containere. Spre deosebire de mașinile virtuale, containerele vor avea în final același kernel și nu sunt legate de nicio infrastructură sau sistemul de operare gazdă.
Figura 4.1.1.1 Arhitectura Docker [10]
Docker-ul folosește arhitectură de tip client-server. Docker client comunică în mod direct cu Docker daemon care realizează operațiile de build, rulare și livrare al containerelor. Docker client și docker daemon pot să ruleze pe același sistem gazdă sau se poate folosi conexiunea remote dacă docker daemon rulează pe alt sistem. Comunicarea dintre client și server se realizează prin sockets sau prin intermediul RESTful API. [15]
4.1.2 Docker images
Imaginile docker sunt fișiere de sistem predefinite ce pot fi folosite de către Docker. De exemplu, o imagine poate să conțină un sistem de operare Ubuntu cu o bază de date nerelațională Mongo, sau un web server. Aceste imagini se construiesc aplicând diferite modificări, update-uri, instalări asupra unei imagini de bază.
Exemplu de imagini existente: "ubuntu:15.04", "mongo:2.2.7", "java:8".
Conform [33], o imagine este formată din unul sau mai multe layere. Fiecare instrucțiune RUN dintr-un fișier Dockerfile creează un nou layer peste layerele existente ale imaginii. Imaginile tind să aibă din ce în ce mai puține pachete, fără interfață grafică(GUI), reprezentând strictul necesar funcționării unui singur proces. Toate layerele deja existente în cadrul unei imagini sunt "read-only", doar layerul superior fiind "writable". Cea mai comodă metodă de a crea o nouă imagine este folosind fișierul Dockerfile.
Exemplu de fișier Dockerfile:[33]
FROM ubuntu:latest
MAINTAINER Stefan Balea <[anonimizat]>
RUN apt-get update
RUN apt-get -y upgrade
ENTRYPOINT /bin/bash
Pentru crearea unei noi imagini se folosește comanda "build". Exemplu: "docker build -d imagine-noua ." Această comandă creează o imagine nouă cu numele "imagine-nouă" pornind de la imaginea ubuntu:latest și executând comenzile de update / upgrade. Dacă la rularea imaginii nu se folosește niciun parametru, se va executa bash-ul de la ENTRYPOINT.
4.1.3 Docker containers
Docker container este o instanță a unei imagini Docker. Pentru a rula un container se folosește comanda "run". Pentru exemplificare, vom starta un container pornind de la imaginea anterior creată. Exemplu: "docker run -d –name container-nou imagine-nouă".
Un container trebuie să ruleze un singur proces sau o singură aplicație, containerul rămânând activ atât timp cât acel proces rulează. Odată cu terminarea procesului, containerul este oprit automat.
Să presupunem că avem o aplicație scrisă în java care este conectată la o bază de date mysql. Pentru rularea acestei aplicații vom aveam nevoie de două containere: un container de mysql care să ruleze o instanță de mysql server, respectiv un container care să ruleze un server tomcat. Pentru a face o conexiune între cele două containere, la rularea containerului tomcat se foloseste comanda "–link". Exemplu: "docker run -d -p 8080:8080 –name tomcat –link=mysql:mysql tomcat-image".
Fiecare Docker container poate rula propria distribuție Linux, indiferent de distribuția de pe calculatorul gazdă. În cazul mai sus prezentat imaginea de mysql poate să conțină un sistem de operare Ubuntu 15.04 pe care rulează mysql server iar imaginea de tomcat poate să conțină un sistem de operare Ubuntu 16.04 pe care rulează serverul tomcat.
4.2 Jersey
Jersey este un framework open source folosit pentru dezvoltarea de servicii RESTful în Java. Acest framework permite implementarea de servicii JAX-RS și de clienți pentru aceste servicii cu un efort de programare minim, prin utilizarea unui număr destul de mare de biblioteci proprii și de la alți furnizori.
Deoarece construirea de servicii cu Jersey implică obținerea de multe dependințe, se necesită de cele mai multe ori folosirea unor sisteme de build și management al proiectelor precum Maven, Gradle. Rularea acestor proiecte pentru prima dată necesită un timp considerabil, timp necesar descărcării dependințelor din depozitul Maven.
4.3 Baze de date NoSQL
În ultimii 23 de ani, bazele de date relaționale sunt modalitatea preferată a arhitecților software pentru a stoca baze de date. Deși, în ultima perioadă însă, bazele de date NoSQL au început să evolueze și să câștige din ce în ce mai mult teren. Aceste baze de date au apărut din necesitatea unor companii uriașe precum Facebook, Twitter sau Google de a manipula și organiza cantități uriașe de date, cantități cărora bazele de date tradiționale nu le făceau față. De aceea, bazele de date NoSQL au fost proiectate pentru stocarea volumelor mari de informații, în general fără o schemă fixă și distribuite pe mai multe servere.
Stocarea informațiilor în bazele de date NoSQL se poate face în mai multe moduri precum: implememtări BigTable, colecții cheie-valoare, colecții de documente și baze de date orientate pe grafuri.[3]
Bazele de date NoSQL prezintă următoarele caracteristici:
abilitatea de a scala orizontal pe mai multe servere, precum și abilitatea de a replica și distribui datele pe mai multe servere;
utilizare eficientă a indexării distribuite și a memoriei RAM pentru o stocare eficientă;
abilitatea de a adăuga dinamic noi atribute la înregistrările existente.
O altă caracteristică importantă a sistemelor NoSQL este arhitectura "share nothing". În această arhitectură, fiecare nod / server este independent, niciunul nu partajează spațiu sau memorie. Datorită acestui avantaj al bazelor de date NoSQL, pot fi efectuate un număr foarte de operații de citire și scriere pe secundă. O simplă operație asupra bazei de date poartă denumirea de OLTP(online transaction processing), operație comună și în cadrul aplicațiilor web moderne. [26]
Totuși, bazele de date NoSQL prezintă și dezavantaje, una dintre cele mai importante fiind faptul că baza de date este formată dintr-un număr foarte mare de relații (tabele). Fiecare relație are o schemă fixă. Modificarea acestei scheme(deci structura virtuală a bazei de date) poate cere modificarea aplicațiilor, uneori cu costuri foarte mari. Totodată, dimensiunea tabelelor poate crește foarte mult(în bazele de date este necesar să fie memorate cantități foarte mari de informații de text sau sub formă binară precum: imagini, audio, film). Un alt dezavantaj ar fi lipsa de maturitate, deoarece majoritatea sistemelor NoSQL sunt la primele versiuni sau chiar în faze incipiente de dezvoltare.
Una dintre cele mai cunoscute baze de date NoSQL este MongoDB. Este o bază de date ușor de înțeles și de manipulat, fiind utilă atât pentru proiecte de dimensiuni reduse cât și proiecte care necesită un volum mare de date stocate.
4.3.1 MongoDB
MongoDB este o bază de date NoSQL, orientată pe obiecte, scrisă în C++. A apărut pentru prima dată în anul 2009 și beneficiază de suport din partea companiei 10gen. Spre deosebire de bazele de date relaționale care folosesc tabele pentru stocarea informațiilor, MongoDB folosește documente dinamice.
Această bază de date prezintă o bună scalabilitate pe orizontală, fiind distribuită pe mai multe noduri. Informațiile sunt stocate în documente având structură similară cu un document XML sau JSON.
Principalele tipuri de date suportate pentru câmpurile unui document sunt:
Tipuri scalare: integer, double, float, boolean;
Tipuri șir de caractere: string, expresii regulate, cod;
Obiecte: similar cu JSON, numite BSON;
Object id: este reprezentat pe 12 octeți și reprezintă unic documentele în baza de date. Perechea de 12 octeți sunt distribuiți în felul următor: primii 4 octeți reprezintă UNIX timestamp, următorii 3 octeți sunt identificatori unici de mașină, următorii 2 octeți reprezintă id-ul procesului iar ultimii 3 octeți reprezintă un numărător, începând cu o valoare random. [16]
Datorită acestei flexibilități, echipa de dezvoltare a unei aplicații poate să modifice în mod rapid structura obiectelor salvate în baza de date. Acest lucru este foarte util într-o aplicație unde se impune în mod continuu modificarea cerințelor existente.
Exemplu de document salvat într-o bază de date Mongo:
{
"nume": "Balea",
"prenume": "Stefan",
"email": [
"[anonimizat]",
"[anonimizat]"
],
"adresa": {
"strada": "Fantanele",
"localitate": "Cluj-Napoca",
"cod": "222222",
"nr": 63,
"scara": "III"
}
}
Termenii MySQL și conceptele MongoDB sunt destul de similare, după cum se poate observa în tabelul următor:
Tabel 4.3.1.1
Atât MySQL, cât și MongoDB oferă pentru utilizare un număr impresionant de interogări. Pentru exemplificare, avem următoarele exemple: [16]
Tabel 4.3.1.2
4.4 Framework-ul Spring
Spring este unul dintre cele mai populare framework-uri open source pentru aplicații Java enterprise.Acesta a fost introdus pentru prima dată în anul 2002 de către Rod Johnson în "Expert One-on-One J2EE Design and Development". În iunie 2003, Spring a intrat sub licența Apache 2.0. În momentul de față, Spring poate fi considerat o platformă alternativă unei platforme Java EE complete, ajungând să includă caracteristici ce se găsesc în mod normal la servere de aplicații complexe. Printre aceste caracteristici amintim: suport pentru tranzacții și securitate, suport pentru servicii web, asigură access de la distanță. [29]
Cardul Spring oferă soluții la multe probleme întâlnite de dezvoltătorii Java și de organizații ce doresc să creeze aplicații pe platforma Java. În primul rând, Spring este o platformă pentru dezvoltarea și rularea aplicațiilor web. Funcționarea acestei platforme poate fi împărțită în două module importante.
Din [29] reiese faptul că nucleul central se ocupă cu configurarea, administrarea și îmbunătățirea componentelor Spring în aplicații. Pe lângă acesta, Enterprise Service Layer oferă funcționalitate și abstractizare asupra caracteristicilor enterprise cum ar fi tranzacții, acces la date, acces la distanță.
Conform [29], comparativ cu cadre similare, unul dintre diferențiatorii cheie al containerului de bază din Spring este acela că elementele adminstrate sunt de obicei POJO( Plain Old Java Object) care nu necesită implementarea de interfețe speciale sau cunoștințe despre ciclul de viață al containerului. Aceste obiecte sunt configurate folosind principiul Inversion of Control(IoC). Folosind acest principiu înseamnă că dezvoltătorii nu leagă direct obiectele prin cod Java, ci se bazează pe container pentru crearea, scrierea și administrarea obiectelor.
Structura clasică pentru o aplicație web este un fișier WAR care conține fișierele JAR necesare. Acest tip de aplicație poate folosi orice container web pentru rulare, însă Spring recomandă Apache Tomcat, datorită faptului că acesta este free, rentabil, are cost minim de hostare a aplicațiilor astfel fiind foarte popular în cadrul dezvoltatorilor web.
4.4.1 Injectarea dependențelor
Aplicațiile Web sunt aplicații complexe, formate din mai multe module ce provin din surse diferite folosind tehnologii diferite. Componentele unei aplicații Web pot fi distribuite pe mai multe mașini dar trebuie să comunice între ele.
Multe aplicații din viața reală au trei layere distincte: interfața cu utilizatorul aplicației, logica aplicației(și obiectele din domeniul aplicației) și partea de lucru cu baza de date. Componentele și tehnologiile pentru fiecare layer s-au modificat în timp. Pe platforma Java, componentele de pe layerul superior pot fi servleți, controllere, clienți de servicii Web, aplicații Android sau aplicații Swing; componentele de pe al doilea layer pot fi obiecte Java diverse(POJO), componente EJB sau componente "Bean", iar pe ultimul layer găsim diverse produse de tip ORM (JPA, Hibernate, Morphia etc), JDBC sau Mongodb. [22]
Un principiu general de proiectare al aplicațiilor complexe este acela că între modulele unei aplicații, cuplajul sau dependența trebuie să fie cât mai slabă, astfel că înlocuirea unui modul cu un altul să nu necesite prea multe modificări în celelalte module care depind de cel înlocuit. Cuplajul prea strâns împiedică și testarea unitară a componentelor, în izolare față de alte componente cu care colaborează în aplicație. [22]
O soluție de realizare a unui cuplaj slab între obiecte este injectarea dependențelor cunoscută ca și Dependency Injection, realizată cu ajutorul framework-ului Spring.
Conform [22], avem următoarele avantaje:
Înlocuirea unui obiect cu altul care respectă aceeași interfață este mai simplă și mai rapidă.
Reutilizarea unor părți dintr-o aplicație în alte aplicații.
La realizarea aplicației pot lucra mai multe persoane sau echipe în același timp.
Întreținerea(modifcarea) codului este destul de simplificată.
Testarea unitară a codului este mult mai simplificată.
4.5 Quartz Scheduler
Quartz este o librărie open-source folosită pentru programarea și executarea unor task-uri. Această librărie poate fi integrată în orice aplicație Java, începând de la aplicațiile mici de sine stătătoate până la aplicațiile e-commerce de mari dimensiuni. Quartz poate fi folosită pentru a crea simple sau complexe planificări pentru rularea a sute, mii sau zeci de mii de task-uri. [19]
Task-urile sunt definite ca fiind componente Java standard construite pentru a rula în mod virtual ceea ce ele sunt programate să realizeze. Librăria Quartz Scheduler include multe caracteristici enterprise precum suport pentru tranzacțiile JTA și clustering. [19]
Quartz poate fi foarte utilă dacă se dorește ca aplicația să ruleze anumite task-uri la anumite intervale de timp. Exemplu de task-uri care pot fi realizate cu ajutorul Quartz:
Conducerea fluxului de lucru al unui proces – dacă s-a realizat o comandă a unui produs, se poate planifica execuția unui task care să ruleze exact la două ore după ce comanda a fost trimisă. Acest task va verifica starea acestei comenzi. Totodată acest task poate declanșa o notificare de avertizare dacă nu s-a realizat o confirmare a comenzii și implicit schimbarea stării comenzii la "în așteptare".
Întreținerea sistemului – planificarea execuției unui task pentru perioada de weekend care să descarce tot conținutul unei baze de date în fișiere XML.
Furnizarea unor servicii de reminder în interiorul aplicației.
Conform [19], Quartz oferă urmatoarele funcționalități:
mediu de rulare divers;
planificarea task-urilor;
executarea task-urilor;
salvarea task-urilor.
După cum putem afla din [19], Quartz poate rula într-un mediu de rulare divers, precum: încapsulat într-o aplicație de sine stătătoare; într-o aplicație server(sau container de servleți); ca și aplicație de sine stătătoare(în propria mașină virtulă Java) fiind accesată cu ajutorul RMI (Remote Method Invocation).
Planificarea task-urilor se poate realiza în diferite moduri, cele mai importante fiind:[19]
la un moment fix al zilei;
în anumite zile ale săptămânii;
în anumite zile ale lunii;
în anumite zile ale anului;
executare repetată de un număr fix;
executare repetată până la o anumită oră/dată fixă;
executare repetată cu un anumit delay între execuții.
Quartz recunoaște un task ca fiind orice clasa Java care implementează interfața Job, funcționalitatea acestui task fiid definită de către programator. Aceste clase pot fi instanțiate de către Quartz sau de către alt framework din interiorul aplicației în care acestea rulează. Când are loc un factor declanșator(Trigger), planificatorul notifică unul sau mai multe obiecte Java care implementează una din interfețele: JobListener sau TriggerListener. Aceste obiecte Java sunt deasemenea notificate odată ce s-a finalizat execuția task-ului.
Quartz oferă o interfață JobStore cate poate fi implementată pentru a realiza variate mecanisme de stocare al task-urilor. Folosind JDBCJobStore, toate task-urile(Jobs) respectiv Trigger-urile configurate ca fiind nevolatile sunt stocate într-o bază de date relațională cu ajutorul JDB. (Java Database Connectivity)
4.6 Framework-ul JUnit
Noțiunea de "testare unitară" se referă la testarea individuală a unor unități separate dintr-o aplicație software. În sistemele orientate pe obiecte, testarea unitară se rezumă la testarea unor clase sau metode.
Tool-urile de testare unitară pun la dispoziția programatorului posibilități de a înregistra și repeta anumite teste, de regulă atunci când are loc o schimbare importantă în sistem. Unul dintre cele mai folosite tool-uri pentru realizarea testării unitare a claselor în Java este framework-ul JUnit.
JUnit este un framework de clase ce permite scrierea și execuția de teste pentru diferite metode, clase din cod. [5]
Conform [34], principalele avantaje ale utilitarului JUnit sunt:
clasele de test sunt relativ ușor de scris și modificat pe măsură ce codul sursă se mărește. În acest fel, compilatorul testează sintaxa codului sursă, în timp ce clasele de test validează integritatea acestuia.
creșterea vitezei de scriere a codului, concomitent cu creșterea calității acestuia. Având teste unitare care acoperă un procent cât mai mare din aplicație, se micșorează timpul de depanare.
clasele JUnit pot fi executate automat(în suită), rezultatele fiind vizibile imediat. În funcție de cerințele proiectului, se pot crea ierarhii de suite de test, ierarhii care pot fi testate împreună sau separat.
JUnit este un utilitar gratuit.
clasele de test măresc încrederea programatorului în codul sursă scris.
Scrierea claselor de test este foarte importantă în realizarea unei aplicații de mari dimensiuni, deoarece ajută foarte mult la depanare. De exemplu, să presupunem că există o funcționalitate a aplicației folosită în diferite contexte a acesteia. Dacă această funcționalitate se modifică, este foarte posibil ca toate funcționalitățile din cadrul aplicației dependente de aceasta să genereze erori. Aceste erori pot fi foarte ușor depistate și depanate, deoarece clasele de test vor genera un semnal de alarmă cu privire la alterarea acelor funcționalități.
4.7 Openfire
Conform [17], Openfire este un server pentru trimiterea de mesaje în mod instant, aflat sub licența Apache. Pentru trimiterea și recepționarea de mesaje, folosește un protocol larg răspândit numit XMPP(sau Jabber). Openfire este relativ ușor de configurat, oferind un nivel ridicat de securitate și peformanță.
Proiectul Openfire a fost creat în jurul anului 2002 de către Jive Software. În prezent este dezvoltat în interiorul unei comunități, făcând parte din cadrul proiectului Ignite Realtime, liderul acestuia fiind Dave Cridland. [27]
Administrarea acestui server se face prin intermediul platformei Web care rulează default pe portul 9090(HTTP), respectiv 9091(HTTPS). Platforrma Web poate fi accesată doar de către administrator, el fiind singurul care poate face modificări asupra configurărilor serverului. [27] Principalele activități pe care le poate realiza administratorul sunt: adăugarea, editarea sau ștergerea unui utilizator, adăugarea, editarea sau ștergerea unui grup sau grup chat, adăugarea, ștergerea sau editarea configurațiilor unui plugin, modificarea unor configurații cu privire la arhivarea mesajelor, etc.
Conform [27], principalele funcționalități ale serverului Openfire sunt:
existența unei platforme web pentru administrarea serverului;
customizabilitatea;
suport SSL/TLS;
conectivitate la o bază de date pentru stocarea mesajelor sau informațiilor utilizatorului. Această bază de date poate să fie embedded (HSQLDB) sau externă(MySQL, PostGreSQL etc) folosind JDBC 3;
conexiune LDAP;
platformă independentă, pur Java;
integrare completă cu Spark(client XMPP);
suportă concomitent peste 50000 utilizatori.
Când vine vorba despre plugin-uri, Openfire este foarte customizabil. Adăugarea unui plugin se poate face în platforma Web din cadrul ferestrei Plugins sau prin adăugarea fișierului cu extensia .jar în sistemul de fișiere al serverului, în folderul denumit "plugins". Exemplu de plugin-uri destul de des utilizate: Search, Registration, UserService, Monitoring etc.
Aceste plugin-uri adaugă funcționalități foarte importante serverului. De exemplu, plugin-ul UserService face ca adăugarea, ștergerea, editarea unui utilizator sau al unui grup de utilizatori să poată fi realizată relativ ușor prin folosirea unor requesturi HTTP de către administrator din cadrul altor aplicații.
Observație: mai multe detalii legate de plugin-urile utilizate în cadrul aplicației precum și utilitatea acestora vor fi prezentate în capitolul următor.
XMPP(Extensible Messaging and Presence Protocol) este o tehnologie open source folosită pentru comunicarea în timp real folosind mesage XML. [20] Fiecare literă din acest acronim are semnificație importantă:
X – eXtensible: fiind un protocol open source, acesta este dispus la modificări, în funcție de cerințele necesare.
M – Messaging: este acea parte din XMPP cu care utilizatorul interacționează. Reprezintă mesajul propriu-zis trimis între doi utilizatori. Mesaje sunt trimise în timp real, folosindu-se un mecanism foarte eficient.
P – Presence: indică serverului dacă un utilizator este online / offline / busy. În termeni tehnici, presence-ul determină starea curentă a unei entități XMPP. În termeni nespecializati, presence-ul determină daca o entitate poate sau nu să primească mesaje.
P – Protocol: înainte de toate, XMPP este un protocol, un set de standarde care permite sistemelor să comunice între ele. Portul standard prin care se face comunicarea în cadrul protocolului XMPP este 5222.
Aplicații uriașe care oferă sistem de chat precum Facebook, WhatsApp folosesc acest protocol sau un protocol modificat pornind de la acesta pentru trimiterea si recepționarea mesajelor.
4.8 Sistemul de operare Android
Android este o platformă software și un sistem de operare pentru dispozitive și telefoane mobile bazată pe nucleul Linux, dezvoltată inițial de compania Google, iar mai târziu de consorțiul comercial Open Handset Alliance. [32]
Cele mai cunoscute medii de dezvoltare utilizate pentru dezvoltarea aplicațiilor compatibile cu sistemul de operare Android sunt: Eclipse IDE, Android Studio 2.0. Android permite dezvoltătorilor să scrie cod gestionat în limbajul Java, pentru controlul dispozitivului folosindu-se bibliotecile Java dezvoltate de Google.
Potrivit [25], primul telefon având sistemul de operare Android a fost HTC Dream introdus publicului larg în Octombrie 2008. Telefonul rula Android 1.0.
Sistemul de operare Android este folosit cu predominanță pentru smartphone-uri, însă în prezent apar din ce în ce mai multe dispozitive care deasemenea lucrează pe bază de Android, dar nu sunt telefoane. Astfel de dispozitive sunt: tabletele, smartwatch-urile [21], smart-tv, smart-books [24], camere, dispozitivele de realitate augmentată (ex: Google Glass). Google a acordat atenție acestui trend de apariție a diverselor dispozitive care suportă Android și a creat API-uri specializate pentru dezvoltarea aplicațiilor pentru ele. De exemplu exista Smart-wear API, Smart-tv API, si Glass Developement Kit & API.
4.8.1 Arhitectura sistemului de operare Android
Sistemul de operare Android este alcătuit din mai multe straturi care comunică între ele, printre cele mai importante fiind: Linux Kernel, Libraries, Application Framework, Applications. Fiecare nivel foloseste serviciile oferite de nivelul anterior lui.
Figura 4.8.1.1 Arhitectura sistemului de operare Android [7]
Nivelul aplicatie include setul de aplicații precum calendar, contacte, mesaje, aplicații Google specifice precum Gmail, Google Calendar, Google Web Search, Google Maps. Toate aceste aplicații pot fi sincronizate cu Google Cloud folosind Google Sync.
Application Framework este acel nivel cu care lucrează direct programatorul. Acest nivel conține servicii predefinite pentru managementul resurselor hardware(display, senzori etc) respectiv pentru managementul resurselor software(alarme. servicii etc). Deasemenea conține și integrarea cu anumite resurse externe cum ar fi localizarea prin GPS, Google Cloud Messaging etc. Acest nivel conține urmatoarele servicii:
Activity Manager: controlează durata de viață a activităților, deține informații despre procesele deschise, task-urile active, serviciile care rulează la un moment dat precum și informații legate de gestionarea memoriei.
Window Manager: este responsabil pentru organizarea display-ului, alocarea de ferestre pentru aplicații respectiv corespondența dintre ferestre și diferite aplicații.
Content Providers: reprezintă o interfață de legatură dintre informațiile unui proces cu informațiile altui proces, oferind totodata și un mecanism de securitate. [13]
Package Manager, Resource Manager, Location Manager and Notification Manager: ajută la managementul anumitor funcționalități precum: accesul la GPS, trimiterea de notificări etc.
Sistemul mai conține și un set de librării scrise in C/C++ OpenGL folosită pentru dezvoltarea aplicațiilor 3D sau un sistem de management al bazelor de date SQLite.
4.8.2 Ce este o activitate?
În Android, o activitate este o clasă care extinde clasa AppCompatActivity și reprezintă o interfață cu utilizatorul. Pentru a fi startate folosind metoda startActivity(), toate activitățile trebuie să fie declarate în interiorul fișierului AndroidManifest.xml. [13] De obicei, fiecare activitate are alocat un fișier cu extensia .xml. Acest fișier se găsește în folderul res.layout și conține elemente grafice ale activității, precum butoane, editoare de text, checkbox-uri etc.
Activitățile în cadrul unei aplicații Android sunt administrate ca și o stivă de activități (FIFO – first in first out). Dacă o nouă activitate este startată, aceasta este plasată pe prima poziție în stivă, devenind activitatea afișată. [13]
Principalele 4 stări pe care o activitate poate să le aibă sunt: [13]
running / active: activitatea se află în prim planul ecranului device-ului(pe prima poziție în stiva de activități).
paused: activitatea este parțial vizibilă; există o altă activitate care ocupă o parte din suprafața ecranului și are prim planul. O activitate care se află în starea "paused" își menține starea respectiv toate informațiile. În foarte rare cazuri aceasta este inchisă de către sistemul de operare daca nivelul de memorie este critic.
stopped: activitatea nu este vizibilă. O activitate care se află în starea "stopped" cel mai probabil va fi închisă de către sistemul de operare.
destroyed: activitatea este închisă de către sistemul de operare sau este ștearsă din memorie.
În figura 4.8.2.1 pot fi observate principalele stări pe care o activitate le poate avea:
Figura 4.8.2.1 Stările unei activități [6]
4.8.3 Ce este un serviciu?
Potrivit [12], un serviciu este o componentă a unei aplicații (extinde clasa Service sau alte subclase ale acesteia) care poate executa operații de lungă durată în background, neavând interfață grafică.
Un serviciu poate lua două forme:
started: o componentă a aplicației(de exemplu o activitate) apelează metoda startService(). Odată startat, acest serviciu poate rula în background pentru o durată lungă de timp, chiar dacă componenta care l-a startat este distrusă. De regulă, acest tip de serviciu execută un singur task cum ar fi descărcarea unui fișier. Odată ce activitatea executată de acesta se termină, serviciul este oprit automat.
bound: acest tip de serviciu este atașat atunci când o componentă a aplicației apelează metoda bindService(). Mai multe componente ale aplicației pot face bind aceluiași serviciu, dar odată ce toate componentele apelează metoda unbindService(), serviciul este distrus.
Cele mai importante metode apelate de către un serviciu sunt: [13]
onStartComand(): metodă apelată când o componentă a aplicației(de exemplu o activitate) apelează metoda startService().
onBind(): metodă apelată când o componentă a aplicației(de exemplu o activitate) apelează metoda onBindService().
onCreate(): metodă apelată când serviciul este creat pentru prima dată. Dacă serviciul deja rulează, această metodă nu mai este apelată.
onDestroy():metodă apelată atunci când serviciul nu mai este folosit sau a fost distrus.
Stările pe care un serviciu le poate avea depinde de tipul de serviciu. În figura 4.8.3.1 pot fi observate aceste stări pentru ambele tipuri de servicii:
Figura 4.8.3.1 Stările unui serviciu [11]
4.8.4 AndroidManifest.xml
Fișierul AndroidManifest.xml este necesar în orice aplicație Android. Conține toate informațiile esențiale despre aplicație precum: numele aplicației, pachetele aplicației, versiunea de Android SDK(Software Development Kit), versiunea de Android minimă și maximă pe care aplicația o suportă. [13]
Pe lângă aceste informații, mai descrie și componentele aplicației: activitățile, serviciile, componentele de broadcast precum și permisiunile necesare aplicației. Exemple de permisiuni: accesul la internet, accesul la camera foto, accesul la informații legate de WI-FI, accesul la memoria internă / cardul de memorie.
Capitolul 5
Proiectare și implementare
5.1 Detalii de proiectare
5.1.1 Cazuri de utilizare
În acest subcapitol vor fi prezentate principalele funcționalități ale sistemului sub forma unor cazuri de utilizare.
Un caz de utilizare este o colecție de scenarii de succes și de eșec care descriu secvența de acțiuni efectuare de un sistem pentru obținerea unui rezultat observabil de către actor. Actorul este o entitate care are un comportament specific, de exemplu o persoană, un alt sistem, etc. În sistemul proiectat, se pot identifica doi actori principali:
Admin: pentru realizarea task-urilor pe care trebuie să le realizeze folosește aplicația BroadcastClient.
Membru: orice utilizator al aplicației se numeste utilizator membru. După logarea în aplicație cu ajutorul device-ului Android, acesta poate realiza o multitutine de activități precum adăugarea unei fotografii, adăugarea de comentarii la o postare a unui prieten, etc.
5.1.1.1 Diagramă cazuri de utilizare administrator
Cazul principal de utilizare constă din efectuarea activității de adăugare a noilor utilizatori de către actor, în cazul de față acesta fiind utilizatorul Admin.
Adăugare nou utilizator.
În figura următoare va fi prezentat cazul de utilizare al utilizatorului Admin:
Figura 5.1.1.1.1 Diagrama cazuri de utilizare pentru Admin
În continuare vom prezenta acest caz de utilizare:
Nume caz de utilizare: Adăugare nou utilizator
Actorul principal: Utilizatorul Admin
Scurtă descriere: De fiecare dată când un utilizator nou dorește să se înregistreze în cadrul aplicației, aplicația BroadcastClient primește un pachet cu informații despre acest utilizator. Pentru adăugarea noului utilizator în cadrul aplicației server principale, se efectuează de către utilizatorul Admin un HTTP request către aplicația server principală cu informațiile noului utilizator.
Succesiunea evenimentelor:
START caz de utilizare
Aplicația BroadcastClient recepționează un nou pachet
Se construiește body-ul folosit pentru requestul către aplicația server principală
Se efectuează HTTP request către aplicația server principală cu autorizare de Admin
După primirea răspunsului, se crează un nou pachet și se trimite răspunul către noul utilizator.
FINALIZARE caz de utilizare
Momentul în care utilizatorul membru este anunțat despre succesul sau eșecul acțiunii sale, cazul de utilizare ia sfârșit.
Cerințe speciale:
Sistemul nu trebuie să permită accesul persoanelor neautentificate, asigurând integritatea informațiilor utilizatorilor existenți.
Precondiții:
Aplicația BroadcastClient trebuie să primească un pachet de la noul utilizator care să conțină data curentă în milisecunde.
Postcondiții:
Aplicația principală de server returnează un răspuns care este trimis printr-un nou pachet către aplicația client.
Daca salvarea s-a realizat cu succes, datele stocate trebuie sa ramână persistente.
Prioritate: este obligatoriu din punctul de vedere al utilizatorului Admin ca atunci când primește un pachet să execute acești pași.
Disponibilitate: disponibilă tot timpul.
Frecvența de utilizare: este variabilă, în funcție de numărul de utilizatori care se înregistrează în cadrul aplicației.
5.1.1.2 Diagramă cazuri de utilizare membru
Cazul principal de utilizare constă din efectuarea următoarelor activități de către actor, în cazul de față acesta fiind utilizatorul membru. Un membru este orice utilizator care folosește aplicația.
Logare în aplicație;
Modificare informații proprii;
Adăugare activitate;
Ștergere activitate;
Adăugare comentariu la activitate;
Ștergere comentariu la activitate;
Vizualizare comentarii existente pentru o activitate;
Distribuire activitate pe propriul profil;
Efectuare rating la o activitate;
Vizualizare utilizatori care au efectuat rating;
Vizualizare listă prieteni;
Vizualizare profil alt utilizator;
Comunicare chat între doi utilizatori.
În figura următoare va fi prezentat cazul de utilizare al unui utilizator membru:
Figura 5.1.1.2.1 Diagrama cazurilor de utilizare pentru un utilizator membru
În continuare se va prezenta în detaliu unul dintre aceste cazuri de utilizare.
Nume caz de utilizare: Adăugare activitate
Actor principal: Utilizator membru
Scurtă descriere: Dupa autentificare, utilizatorul va fi redirecționat către pagina principală a aplicației numită News Feed unde are opțiunea de adăugare de nouă activitate.
Succesiunea evenimentelor:
START caz de utilizare
Utilizatorul pornește aplicația pe telefonul mobil cu sistem de operare Android.
Acesta se autentifică în aplicație.
După autentificare se deschide pagina principală a aplicației unde are opțiunea de adăugare a unei noi activități.
Utilizatorul inserează în casuța de text un mesaj pe care dorește să il posteze.
Utilizatorul apasă butonul reprezentat printr-o cameră foto.
Utilizatorul are două opțiuni: alegerea unei fotografii din galeria foto, sau realizarea unei noi fotografii cu ajutorul camerei foto.
Utilizatorul alege o fotografie din galerie.
Culoarea butonului reprezentat de camera foto iși schimbă culoarea.
Utilizatorul apasă butonul "POST".
Un mesaj de succes este afișat daca postarea s-a adăugat cu succes.
FINALIZARE caz de utilizare
Cazul de utilizare ia sfârșit când apare mesajul de succes.
Excepții:
Utilizatorul nu se poate loga în aplicație deoarece nu există aplicația server în această rețea ad-hoc.
Această problemă nu poate fi remediată de către client.
Utilizatorul apasă butonul "POST" fără a adăuga o poză sau text.
Aplicația Android va afișa un mesaj prin care avertizează utilizatorul despre validitatea datelor.
Se va continua de la pasul 4.
Cerințe speciale:
Sistemul nu trebuie să permită accesul persoanelor neautorizate, asigurând integritatea datelor.
Precondiții:
Aplicația să fie instalată pe un dispozitiv ce rulează o versiune de Android mai actuală decât 4.4.2.
Dispozitivul trebuie să fie conectat la un Access-Point. (router)
Postcondiții:
Aplicația revine la pagina principală.
Datele sunt trimise spre aplicația server principală.
Prioritate: rămâne la latitudinea utilizatorului dacă dorește sau nu să folosească această funcționalitate.
Frecvența de utilizare: relativă, rămâne la latitudinea utilizatorului.
5.1.2 Diagrame de secvență
Diagrama de secvență descrie interacțiunea dintre două sau mai multe entități și ordinea în care mesajele sunt schimbate între acestea. Totodată arată modul în care obiectele comunică într-un anumit scenariu de utilizare.
În continuare vor fi prezentate diagramele de secvență pentru trei dintre cele mai importante funcționalități ale aplicației:
Logare în cadrul aplicației;
Adăugare activitate noua;
Efectuare rating.
5.1.2.1 Diagramă de secvență pentru logare în aplicație
Diagrama de secvență este ilustrată în figura 5.1.2.1.1 .
Figura 5.1.2.1.1 Diagrama de secvență pentru logarea în cadrul aplicației
AuthenticationActivity afișează un dialog prin care îi oferă utilizatorului posibilitatea de căutare a unei aplicații server în rețeaua la care este conectat dispozitivul Android. Dacă acesta este de acord, AuthenticationActivity apelează un serviciu care trimite un mesaj de tip broadcast către toate dispozitivele din acea rețea. În acest fel, aplicația BroadcastClient primește un nou pachet de date, pachet interpretat ca fiind de la un nou client. Se instanțiază un nou thread numit HandleNewUser care preia pachetul primit. Se verifică autenticitatea pachetului și se face un HTTP request către aplicația server principală cu informații despre noul utilizator, apelându-se resursa UserDetailWS. În continuare, cererea este trimisă către UserDetailService care face validările de rigoare(verifică daca IP-ul utilizatorului există deja în baza de date). În cazul în care IP-ul există deja în baza de date, se aruncă o excepție care este mapată într-un obiect de tip Response și este trimisă înapoi către aplicația BroadcastClient. Dacă IP-ul nu există în baza de date, utilizatorul primește un nume default și se face o cerere către UserDetailDao prin care noul utilizator este salvat în baza de date. Dacă salvarea în baza de date s-a realizat cu succes, utilizatorului i se crează un cont pe Openfire apelându-se XmppService, având ca și "username" ObjectId-ul din baza de date, iar ca și "password" token-ul de autentificare. Valorea booleană returnată de XmppService este setată atributului "xmppSynchronized" și inserată în baza de date.
UserDetailService returnează id-ul noului utilizator resursei UserDetailWS, care trimite răspunsul aplicației BroadcastClient. Răspunsul este procesat de către firul de execuție HandleNewUser care trimite un pachet către dispozitivul care a trimis broadcast-ul. Dispozitivul poate să primească unul din mesajele: "Success", "Already exists" sau "Server error". Mesajul "Success" simbolizează faptul că înregistrarea utilizatorului s-a realizat cu succes, "Already exists" simbolizează faptul că acest IP deja figurează în baza de date pentru alt utilizator iar mesajul "Server error" simbolizează faptul că aplicația server există în rețea dar nu returnează niciun răspuns.
Dacă AuthenticationActivity nu primește niciun pachet în interval de 10 secunde, se afișează un mesaj de eroare cu mesajul "No application found in this area".
5.1.2.2 Diagramă de secvență pentru adăugare activitate nouă
Diagrama de secvență este ilustrată în figura 5.1.2.2.1 .
Figura 5.1.2.2.1 Diagramă de secvență pentru adăugare nouă activitate
Utilizatorul apasă butonul simbolizat printr-o cameră foto. La apăsarea acestui buton, apare un dialog prin care acesta este solicitat să aleagă între adăugarea unei fotografii existente din Galerie sau realizarea unei noi fotografii folosind Camera dispozitivului. La alegerea opțiunii de realizare a unei noi fotografii, se trimite o cerere către Camera. După realizarea fotografiei, utilizatorul are posibilitatea de a realiza o nouă fotografie dacă cea deja realizată nu este pe placul acestuia, sau de a folosi fotografia existentă.
Odată ce fotografia a fost realizată cu succes, este salvată în galeria telefonului într-un director numit Social. Camera returnează calea completă spre această fotografie, totodată butonul simbolizat prin camera foto modificându-si culoarea în roșu. Utilizatorul are posibilitatea de vizualizare a acestei fotografii, având două opțiuni: de a folosi această fotografie apăsând butonul "Use", sau în caz contrar butonul "Cancel" dacă acesta nu dorește
utilizarea acestei fotografii. Dacă s-a accesat butonul "Cancel", fotografia este ștearsă din galeria dispozitivului. Dacă s-a acccesat butonul "Use", butonul simbolizat prin camera foto iși va schimba culoarea în roșu. Acest lucru simbolizează faptul că odată apăsat butonul POST, imaginea va fi trimisă către server, apelându-se resursa PhotoDetailWS, trimițând cererea spre PhotoDetailService.
PhotoDetailService face verificările de rigoare(apelând metode din PhotoDetailServiceUtilImpl care validează tipul fotografiei respectiv integritatea acesteia). O fotografie poate să fie de tipul: POST, PROFILE sau BACKGROUND. Dacă fotografia este de tip POST, aceasta este utilizată în cadrul unei activități, dacă este de tip PROFILE este utilizată ca și fotografie de profil iar dacă este BACKGROUND este utilizată ca și fotografie de background în cadrul profilului.
PhotoDetailDao salvează fotografia în baza de date, returnând ObjectId-ul acesteia care este trimis către PhotoDetailService iar de aici spre PhotoDetailWS. În cadrul clasei PhotoDetailWS se construiește un hashmap care conține ObjectId-ul fotografiei ce este returnat utilizatorului.
Odată ce salvarea fotografiei s-a realizat cu succes, se trimite un nou request cu informațiile activității resursei UserActivityWS. Cererea ajunge în UserActivityService unde se realizează validările de rigoare. După validări, noua activitate este salvată în baza de date cu ajutorul UserActivityDao. Dacă adăugarea s-a realizat cu succes, UserActivityDao returnează ObjectId-ul activității. ObjectId-ul este trimis spre UserActivityWS care returnează răspunsul către client. La primirea răspunsului, clientul afișează un mesaj de succes.
5.1.2.3 Diagrama de secvență pentru efectuare rating
Utilizatorul are posibilitatea de a face rate la o activitate proprie sau postată de alt utilizator al aplicației. În cadrul activității există un atribut numit "Your rating" care conține un rating bar cu valori între 0.5 și 5, unde valoarea 0.5 reprezintă jumatate de steluță iar valoarea maximă 5 reprezintă 5 steluțe. Odată ce utilizatorul a efectuat rating-ul, se face un HTTP request către resursa RatingWS, care trimite cererea spre RatingService.
RatingService face verificările de rigoare apelând metode din clasa RatingServiceUtilImpl. Odată ce verificările au fost realizate cu succes, se trimite o cerere spre RatingDao pentru salvarea rating-ului în baza de date. Daca salvarea s-a realizat cu succes, RatingDao returneaza ObjectId-ul rating-ului.
Fiecare activitate are un atribut numit "generalRating" care reprezintă media aritmetică a rating-urilor. La fiecare rating efectuat pentru o activitate, atributul "generalRating" se schimbă. La fel se va întâmpla și în acest caz, rating-ul general al activitității modificându-se prin apelarea metodei "updateGeneralRating" din cazul clasei RatingService. În urma modificării rating-ului general, se returnează spre RatingWS ObjectId-ul rating-ului salvat care returnează răspunul către client.
Utilizatorul va observa modificarea cu succes al atributului "generalRating" cu noua valoare salvată în baza de date.
Diagrama de secvență este ilustrată în figura 5.1.2.3.1 .
Figura 5.1.2.3.1 Diagrama de secvență pentru efectuare rating
5.2 Detalii de implementare
Sistemul din lucrarea de față este compus din două componente majore, fiecare având un rol bine stabilit încă de la început: partea de server sau back-end-ul aplicației și partea de client sau front-end-ul aplicației.
Partea de server este compusă la rândul ei din două aplicații: o aplicație de broadcast numită BroadcastClient și aplicația principală de server care rulează în docker. Comunicarea dintre cele două aplicații server se face pe baza arhitecturii REST., folosind protocolul HTTP. Pentru dezvoltarea acestei aplicații s-a folosit mediul de dezvoltare Intellij IDEA 14.4.
Partea de client este reprezentată de aplicația Java realizată pentru dispozitive inteligente cu sistem de operare Android, fiind destinată exclusiv utilizatorilor. Pentru dezvoltarea acestei aplicații s-a folosit mediul de dezvoltare Android Studio 2.0.
După cum se poate observa și din diagrama de deployment, comunicarea dintre cele două componente se realizează pe baza arhitecturii REST, folosind protocolul HTTP. Conținutul cererilor și răspunsurile vor fi transmise în format JSON fiind ușor de parsat de către client.
Prin alegerea arhitecturii REST și a celei client-server se realizează o separare uniformă a componentelor sistemului și a atribuțiilor fiecăruia. Astfel, stocarea și procesarea datelor este principala atribuție a serverului, iar interfața cu utilizatorul intră în atribuția clientului. În acest fel, fiecare componentă poate fi dezvoltată independent și ar putea fi înlocuită cu ușurință, atât timp cât se păstrează interfața de comunicare dintre ele, dovedindu-se astfel simplitatea și scalabilitatea sistemului. Sistemul de față își dovedește arhitectura încă de la început, prin faptul că toate componentele au fost dezvoltate independent. Totuși, pe lângă protocolul HTTP folosit între aplicația principală de server și aplicația Android, mai avem două protocoale folosite: protocolul UDP folosit între aplicația Android și aplicația server secundară, respectiv protocolul TCP folosit pentru comunicarea dintre aplicația Android și serverul de chat, Openfire.
În figura 5.2.1 este prezentată diagrama de deployment a întregului sistem, evidențiind principalele componente prezentate anterior.
Figura 5.2.1 Diagrama de deployment a sistemului
În continuare, se vor prezenta în detaliu fiecare componentă din diagrama de deployment, evidențiind comunicarea dintre componentele din interiorul sistemului.
5.2.1 Aplicația server principală
5.2.1.1 Arhitectura bazei de date
Pentru stocarea și organizarea datelor vehiculate în interiorul sistemului se folosește o bază de date NoSQL numită MongoDB. Este o bază de date ușor de înțeles și de manipulat, fiind utilă atât pentru proiecte de dimensiuni reduse cât și proiecte care necesită un volum mare de date stocate.
În figura 5.2.1.1 este prezentată arhitectura bazei de date evidențiindu-se legăturile dintre colecții. În continuare, se va analiza fiecare colecție, evidențiind structura lor precum și legăturile cu celelalte colecții.
Figura 5.2.1.1 Arhitectura bazei de date
După cum se poate observa în figură, avem 5 colecții: Users, Pictures, Activities, Comments și Ratings.
5.2.1.1.1 Colecția Users
În această colecție sunt stocate principalele date de identificare a unui utilizator, printre care se regăsesc: informații personale precum nume, prenume, fotografie de profil, fotografie de background, data activării contului dar și anumite informații utile serverului. Principalele câmpuri ale colecției sunt:
_id;
firstname;
lastname;
tokenId;
activationTime;
mobileIP;
roles;
lastPingTime;
profilePictureId;
backgroundPictureId;
xmppSynchronized.
Unele informații sunt completate direct de către client precum: firstname, lastname iar restul sunt completate de către server. De exemplu, activationTime, tokenId, roles, xmppSynchronized sunt completate de către server la logarea utilizatorului în cadrul aplicației, iar lastPingTime este modificat de server de fiecare dată când se primește un request de heartbeat de la utilizator.
5.2.1.1.2 Colecția Pictures
Această colecție conține sub formă binară toate fotografiile postate de utilizatori în cadrul aplicației. Principalele câmpuri ale colecției sunt:
_id;
userId: reprezintă ObjectId-ul sub formă de String al utilizatorului care a încărcat fotografia;
currentPicture: reprezintă fotografia sub formă binară;
imageType: reprezintă tipul fotografiei postate;
postTime: reprezintă data în milisecunde la care a fost adăugată fotografia.
O fotografie încărcată în cadrul aplicației poate fi de una din cele trei tipuri: "PROFILE", "BACKGROUND" sau "POST". Dacă se dorește modificarea fotografiei de profil, la încărcarea acesteia se va specifica tipul "PROFILE"; dacă se dorește modificarea fotografiei de background, la încărcarea acesteia se va specifica tipul "BACKGROUND" iar daca se dorește folosirea unei fotografii în cadrul unei activități, se va specifica tipul "POST";
5.2.1.1.3 Colecția Activities
În această colecție sunt stocate principalele informații ale unei activități. Principalele câmpuri ale colecției sunt:
_id;
userId: reprezintă ObjectId-ul sub formă de String al utilizatorului care a postat activitatea;
title: reprezintă titlul activității;
description: reprezintă descrierea adăugată de utilizator;
activityType: reprezintă tipul activității;
postTime: reprezintă data în milisecunde la care a fost adăugată activitatea;
generalRating: reprezintă rating-ul general al activității.
pictureId: reprezintă ObjectId-ul fotografiei postate.
O activitate poate fi de două tipuri: "POST_ACTIVITY" respectiv "SHARE_ACTIVITY". Dacă utilizatorul adaugă o activitate nouă, tipul acesteia va fi "POST_ACTIVITY" iar dacă acesta distribuie o activitate existentă, tipul activității nou create va fi "SHARE_ACTIVITY.
În funcție de tipul activității, aplicația server completează automat câmpul "title" al activității. Dacă activitatea postată are tipul "POST_ACTIVITY" și conține fotografie, atunci câmpul "title" va avea valoarea " added new photo.". Dacă activitatea postată are tipul "POST_ACTIVITY" dar nu conține fotografie, câmpul "title" va avea valoarea "". Dacă activitatea postată are tipul "SHARE_ACTIVITY", atunci câmpul "title" va avea valoarea " shared an activity.".
În cadrul câmpului "generalRating" este stocat rating-ul general al unei activități. Rating-ul general reprezintă media aritmetică a rating-urilor unei activități. Valoarea minimă pe care poate să o primească generalRating este 0 iar valoarea maximă este 5. De fiecare dată când se adaugă un nou rating, se recalculează rating-ul general și se updatează valoarea acestuia.
5.2.1.1.4 Colecția Comments
Această colecție conține principalele informații ale unui comentariu. Principalele câmpuri ale colecției sunt:
_id ;
userId: reprezintă ObjectId-ul sub formă de String al utilizatorului care a adăugat comentariul;
activityId: reprezintă ObjectId-ul sub formă de String al activității la care s-a adăugat comentariul;
comment: reprezintă comentariul text adăugat de utilizator;
postedTime: reprezintă data în milisecunde la care a fost adăugat comentariul.
5.2.1.1.5 Colecția Ratings
În această colecție sunt stocate principalele informații necesare efectuării unui rating. Principalele câmpuri ale acestei colecții sunt:
_id;
activityId: reprezintă ObjectId -ul sub formă de String al activității;
userId: reprezită ObjectId-ul sub formă de String al utilizatorului care dorește să efectueze rating.
value: valoarea rating-ului. Această valoare poate avea valori între 0 și 5.
5.2.1.2 Diagrama de pachete
Aplicația principală de server se bazează pe paradigma programării orientată pe obiecte utilizând tehnologii Java precum Jersey, Spring, Quartz etc.
Soluția aplicației server principale este alcătuită din trei mari pachete, fiecare pachet reprezentând un layer din arhitectura 3-tier: presentationLayer, businessLogicLayer, dataAccessLayer.
Pachetul "dataAccessLayer" conține API-urile necesare pentru accesarea bazei de date, pachetul "businessLogicLayer" conține partea de business logic a aplicației iar pachetul "presentationLayer" conține API-urile REST care vor fi apelate de către client.
În cadrul acestui subcapitol, vor fi prezentate în mod succint fiecare dintre aceste pachete.
Pachetul dataAccessLayer
Acest pachet reprezintă layer-ul de bază al arhitecturii 3-tier. Conține mecanisme de stocare a datelor, precum și accesul la acestea. La rândul lui, pachetul conține câteva subpachete: "constants", "dao", "entity" și "resource".
Pachetul "constants" conține clase cu rol auxiliar în aplicație, precum clasele de tip Enum, dar și clase care conțin declarații statice ale unor valori default din cadrul aplicației. Pachetul "dao" conține clase necesare conectării aplicației la baza de date precum și un set de interfețe cu implementările de rigoare care interacționează cu baza de date.
Pachetul "entitites" conține modelele necesare în cadrul aplicației. Fiecare clasă din acest pachet este adnotată cu "@Entity" ceea ce denotă faptul că aceste clase sunt folosite la operații de persistență.
În figura 5.2.1.2.1 este prezentat modul de comunicare dintre cele trei pachete:
Figura 5.2.1.2.1
Diagrama de pachete
Figura 5.2.1.2.2 prezintă în mod succint diagrama de clase din cadrul pachetului "entities".
Figura 5.2.1.2.2 Diagrama de clase din cadrul pachetului entities
Figura 5.2.1.2.3 prezintă diagrama de clase din cadrul pachetului "dao".
Figura 5.2.1.2.3 Diagrama de clase din cadrul pachetului "dao"
Se pot observa interfețele UserDetailDao, UserActivityDao, PhotoDetailDao, CommentDao, RatingDao precum și implementările acestor interfețe. Clasa GenericDatabase este o clasă abstractă care conține metode generice uzuale pentru operații asupra bazei de date. Fiecare clasă care extinde această clasa generică se bucură de implementările metodelor existente în cadrul acestei clase.
După cum am specificat în capitolele anterioare, în dezvoltarea aplicației s-a folosit o bază de date NoSQL. Pentru implementarea interogărilor necesare, s-a folosit driverul Morphia.
Figura 5.2.1.2.5 prezintă diagrama de clase din cadrul pachetelor "constants / resources". Pachetul "resource" conține patru clase extrem de importante: CORSRequestFilter, CORSResponseFilter, MySecurityContext, SecurityInteceptor.
Clasele CORSRequestFilter, CORSResponseFilter realizează mecanismul de CORS. (Cross-origin resource sharing) Acest mecanism permite aplicațiilor din alte domenii să acceseze serviciile REST. Acest lucru se realizează prin adăugarea unor headere în răspunsul trimis clientului. (excepție făcând requestul de OPTIONS).
Figura 5.2.1.2.4 Implementare CORS
Figura 5.2.1.2.5 Diagrama de clase din cadrul pachetelor "resources / constants"
Clasele SecurityInterceptor, MySecurityContext se ocupă de layer-ul securitate. Dorim ca serviciile REST să fie accesate doar de către utilizatorii din cadrul aplicației. De aceea, fiecare utilizator inregistrat în aplicație are "rol" de "USER", exceptie fiind utilizatorul "admin" care are "rol" de "ADMIN". Fiecare REST API are atașat una dintre adnotările:
@RolesAllowed({}): specifică rolul pe care un client trebuie să îl dețină pentru a accesa acest serviciu;
@PermitAll: speficică faptul că orice utilizator poate să acceseze acest serviciu.
@DenyAll: specifică faptul ca niciun utilizator nu poate accesa acest serviciu.
Clasa SecurityInterception implementează interfața ContainerRequestFilter și suprascrie metoda "filter". În cadrul acestei metode se verifică adnotarea atașată serviciului. Dacă serviciul este adnotat cu @PermitAll, atunci utilizatorului i se permite să acceseze resursa fără autentificare. Dacă serviciul este adnotat cu @DenyAll, atunci utilizatorului nu i se permite să acceseze resursa, serverul returnând în format JSON mesajul "Access denied for this resource". Dacă serviciul este adnotat cu @RolesAllowed, serverul trebuie să verifice dacă utilizatorul are drepturile necesare pentru a accesa acest serviciu. Pentru aceasta, utilizatorul trebuie să adauge în request un header cu cheia "tokenId" și token-ul generat la logare. Token-ul este format din concatenarea IP-ului și valorii în timestamp trimisă de către client la logare, criptat cu ajutorul MD5(Message Digest Algorithm 5).
Serverul verifică dacă există un utilizator în baza de date cu acest token. Dacă nu există, serverul răspunde cu mesajul "Access denied for this resource". Dacă utilizatorul există, se creează un obiect de tip MySecurityContext care primește ca și atribut toate informațiile despre utilizatorul curent. Setând security context se pot accesa informațiile utilizatorului care a efectuat requestul în layer-ul presentation, adnotând un atribut SecurityContext cu @Context și folosind metoda "getUserPrincipal()".
Pachetul businessLogicLayer
Acest pachet reprezintă layer-ul de mijloc în cadrul arhitecturii 3-tier. Conține partea de business a aplicației. La rândul lui, acest pachet conține câteva subpachete: "errorHandling", "intialization", "quartz", "service", "util".
Pachetul "errorHandling" conține clasele necesare tratării erorilor. De fiecare dată când apare o excepție în cadrul aplicației de care utilizatorul trebuie să știe, această eroare este mapată într-un obiect de tip Response împreună cu codul erorii și returnată către client.
Pachetul "initialization" conține clasa "InitializationBean" folosită pentru adăugarea utilizatorului "admin" în aplicație. Pachetul "quartz" conține clasele necesare rulării job-urilor în cadrul aplicației. Pachetul "util" conține diferite clase de validare precum și clase auxiliare pentru efectuarea de requesturi spre alte servere.
Pachetul "service" conține principalele clase de business ale aplicației. La fel, conține subpachete precum: "comment", "photoDetail", "rating", "userActivity", "userDetail", "xmpp".
În figura 5.2.1.2.6 este reprezentat modul de comunicare dintre pachetele din interiorul pachetului "businessLogicLayer".
Figura 5.2.1.2.6 Diagrama de pachete ale pachetului "businessServiceLayer"
Într-o formă mai detaliată, diagramele de clase din cadrul pachetului "businessServiceLayer" sunt prezentate în figurile 5.2.1.2.7 respectiv 5.2.1.2.8.
Figura 5.2.1.2.7 Diagrama de clase din cadrul pachetelor "service / errorHandling / util"
După cum am specificat anterior, pachetul "service" conține principalele clase de business ale aplicației. O clasă de o importanță deosebită este "XmppServiceImpl" care implementează interfața "XmppService". Această conține metodele de adăugare / editare / ștergere a unui utilizator din serverul de chat Openfire. Când un utilizator este adăugat în aplicație, automat i se crează un cont pe serverul Openfire apelându-se metoda "addUser(UserDetail userDetail)" din această clasă. Utilizatorul este automat adăugat într-un grup numit "Social". Analog, când se apelează serviciul de editare utilizator, în cadrul metodei de editare din service layer se efectuează și un HTTP request către serverul Openfire cu noile informații.
Exemplu de URI-uri folosite:
http://${openfire.host}:9090/plugins/userService/users/%username% – serviciu de tip DELETE prin care se șterge un utilizator de pe serverul Openfire. Observație: "%username%" trebuie înlocuit cu "username"-ul utilizatorului, adică ObjectId-ul acestuia din baza de date.
http://${openfire.host}:9090/plugins/userService/users/%username_to_update% – serviciu de tip PUT prin care se editează informațiile unui utilizator pe serverul Openfire. Body-ul folosit:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<user>
<name>%new_name%</name>
</user>
Pachetul "errorHandling" conține clasele necesare implementării sistemului de tratare al erorilor. Conține trei clase, "AppException", "ErrorMessage" și "AppExceptionMapper". De fiecare dată când apare o excepție în cadrul aplicației, se va arunca o exceptie de tip "AppException". Se va specifica status code-ul excepției precum și un mesaj semnificativ pentru client. De exemplu, dacă se efectuează o ceree pentru un utilizator care nu există, serverul va returna un mesaj sub formă de JSON având status code-ul 404 și mesajul "User not found".
Clasa "AppExceptionMapper" implemetează interfața "ExceptionMapper" și suprascrie metoda "toResponse". Metoda primește ca și parametru o excepție de tipul "AppException" pe care o mapează pe un obiect de tip "Response" având ca și status code și mesaj informațiile primite de la "AppException".
Pachetul "util" conține clasele de validare corespunzătoare fiecărui pachet din interiorul pachetului "service". Astfel avem următoarele clase de validare: "ActivityServiceUtilImpl", "CommentServiceUtilImpl", "PhotoDetailServiceUtilImpl", "RatingServiceUtilImpl", "UserDetailServiceUtilImpl".
Figura 5.2.1.2.8 Diagrama de clase din cadrul pachetelor "quartz / initialization"
Pachetul "intialization" conține clasa "InitializationBean". La startarea aplicației server(rularea fișierului .war în servlet containerul Tomcat) se apelează metoda "onApplicationEvent(ContextRefreshEvent contextRefreshEvent)". În cadrul acestei metode se citesc informațiile default ale utilizatorului "admin" stocate în fișierul de proprietăți numit "admin_initialization.properties".
Figura 5.2.1.2.9 Conținutul fișierului "admin_initialization.properties"
Se verifică dacă utilizatorul "admin" există în baza de date. Dacă acesta nu există, se creează un obiect de tip UserDetail și se setează valorile necesare. (ip, tokenId, fotografiile de profil / background citite din folderul "default_images" aflat în folderul "resources") și i se atribuie rolul "ADMIN", spre deosebire de restul utilizatorilor care au rolul "USER". Dacă baza de date nu suferă modificări, la următorul deploy, utilizatorul "admin" se va afla deja în baza de date.
Pachetul "quartz" conține clasele "HeartJob", "HearbeatServiceImpl" cea din urmă fiind implementarea interfeței "HeartBeatService" aflată în cadrul aceluiași pachet. Clasa "HeartBeatServiceImpl" implementează metoda "checkEveryUser()" din cadrul interfeței "HeartBeatService". Această metodă ia toți utilizatorii existenți din baza de date(excepție fiind utilizatorul "admin"). Pentru fiecare utilizator se verifică data la care acesta a efectuat un request de "heartbeat". Dacă in ultimele 60 de secunde nu a efectuat niciun request de "heartbeat" rezultă că acesta s-a delogat din aplicație, prin urmare trebuie șters. Metoda este folosită în cadrul clasei "HeartJob".
Clasa "HeartJob" este programată să ruleze ca și task(job) folosindu-se librăria QuartzScheduler. În fișierul "quartz-context.xml" se definește clasa HeartJob ca și job împreună cu metoda "checkTest". Conținutul fișierului este prezentat în figura următoare:
Figura 5.2.1.2.10 Conținutul fișierului "quartz_context.xml"
După cum se poate observa și din fișierul prezentat, acest task va rula cu o întarziere la prima rulare de o secundă iar intervalul la care se va repeta execuția task-ului este de un minut. Totodată, se setează anumite proprietăți asupra obiectului SchedulerFactoryBean care va face trigger la task: este setat numărul maxim de thread-uri la 5 iar prioritatea acestora este 4.
Pachetul presentationLayer
Acest pachet reprezintă nivelul superior din arhitectura 3-tier. La nivelul acestui pachet se definesc resursele care vor fi apelate de către client. Conține diferite subpachete: "comment", "photoDetail", "rating", "test", "userActivity", "userDetail". În figura următoare poate fi vizualizată diagrama de clase din cadrul acestui pachet:
Figura 5.2.1.2.11 Diagrama de clase din cadrul pachetului "presentationLayer"
După cum se poate observa din diagrama de clase, fiecare clasă model are o clasă de webservice la nivelul acestui pachet. Fiecare din aceste clase apelează clase din pachetul "businessLogicLayer" pentru a realiza funcționalitățile cerute. Singura clasă care nu face un astfel de apel este clasa "TestApp", clasă realizată strict pentru a verifica dacă serverul rulează cu success. Tot la acest nivel se poate observa atributul clasei "Security Context", despre care s-a discutat în cadrul pachetului "dataAccessLayer".
5.2.1.3 Injectarea dependențelor
Injectarea dependențelor în cadrul aplicației se realizează cu ajutorul framework-ului Spring. Astfel, crearea obiectelor, durata lor de viață și configurările necesare sunt realizate de către containerul Spring. Orice obiect care este instanțiat, ansamblat și configurat de către containerul Spring se numește "bean". Pentru declararea acestor bean-uri s-a folosit fișierul "applicationContext.xml" aflat în folderul "resources".
Conținutul acestui fișier poate fi vizualizat în figura următoare:
Figura 5.2.1.3.1 Conținutul fișierului "applicationContext.xml"
Acest fișier conține importurile de fișiere necesare în aplicație: "quartz-context.xml", "database-configuration.properties", "xmpp-configuration.properties". Comanda "component-scan" transmite containerului Spring locațiile pe care trebuie să le scaneze.
Pentru a defini bean-urile, s-au folosit adnotările specifice Spring: "@Repository", "@Service", "@Component". Fiecare clasă care implementează interfața specifică a fost adnotată cu una din cele trei adnotări în functie de layer-ul în care se află: în cadrul pachetului "dataAccessLayer" s-a folosit adnotarea "@Repository", în cadrul pachetului "businessLogicLayer" s-a folosit adnotarea "@Service" iar în cadrul pachetului "presentationLayer" s-a folosit adnotarea "@Component".
Pentru "injectarea" bean-urilor se folosește adnotarea "@Autowired".
Figura 5.2.1.3.2 Folosirea adnotărilor "@Service" / "@Autowired"
Totodată, folosind funcționalități specifice framework-ului Spring se pot citi și informații din fișiere de configurare. De exemplu, toate URI-urile spre Openfire care trebuie folosite în clasa "XmppServiceImpl" s-au citit din fișierul de proprietăți "xmpp-configuration.properties". Conținului fișierului poate fi vizualizat în figura 5.2.1.3.3.
Figura 5.2.1.3.3 Conținutul fișierului "xmpp-configuration.properties"
În figura 5.2.1.3.4 este prezentat modul de accesare al acestor informații folosind adnotarea "Value".
Figura 5.2.1.3.4 Accesarea informațiilor din fișierul de configurare
5.2.2 Aplicația server secundară
În ceea ce privește sistemul de logare, aplicația server secundară reprezintă puntea de legătură dintre aplicația client și aplicația server principală. Pentru înregistrarea unui nou utilizator în aplicație, aplicația client trimite un mesaj de tip broadcast către toate dispozitivele din acea rețea. Acest pachet conține data la care s-a trimis pachetul în format timestamp. În acest fel, aplicația BroadcastClient primește un nou pachet de date, pachet interpretat ca fiind de la un nou client. Se instanțiază un nou thread numit HandleNewUser care preia pachetul primit.
Diagrama de clase este prezentată în figura următoare.
Figura 5.2.2.1 Diagrama de clase a aplicației secundare
În cadrul clasei HandleNewThread se efectuează un HTTP request către aplicația server principală cu informațiile despre noul utilizator: IP-ul acestuia și timestamp-ul trimis de către client. Ambele câmpuri sunt obligatorii, altfel aplicația server principală va returna o eroare. Acest request este autorizat și poate fi efectuat doar de către utilizatorul "admin". Aplicația server principală va returna unul din cele două status code-uri: 201 (în acest caz, logarea s-a efectuat cu succes) sau 400 (în acest caz, utilizatorul deja există în aplicație). Dacă apare o eroare în efectuarea request-ului(apare o excepție de tip HttpClientException), aplicația server secundară va trimite acest request de maxim 5 ori. În funcție de răspunsul primit de la server, aplicația Broadcast trimite un pachet spre dispozitiv cu unul dintre cele trei mesaje:
"Success" – utilizatorul a fost înregistrat cu succes;
"Already exists" – IP-ul dispozitivului este deja înregistrat în aplicație;
"Server error" – aplicația server principală există în cadrul rețelei dar nu returnează răspuns.
Majoritatea serviciilor oferite de aplicația server principală sunt securizate, dar neavând un formular standard de logare în cadrul aplicației este necesară construirea aceluiași token de autorizare atât pe client cât și pe server. După cum s-a prezentat în cadrul subcapitolului anterior, token-ul de autorizare este format din concatenarea IP-ului dispozitivului înregistrat și un timestamp, criptate folosind MD5(Message-Digest algorithm 5). Motivul trimiterii acestui timestamp prin broadcast spre aplicația principală este construirea identică a token-ului pe ambele platforme. În acest fel, dacă logarea s-a efectuat cu succes, atât aplicația server principală cât și aplicația client vor avea același token salvat.
5.2.3 Aplicația Android
5.2.3.1 Diagrama de pachete
Soluția aplicației server client este alcătuită din trei mari pachete, fiecare pachet reprezentând o componentă în arhitectura MVC: model, view, controller. Diagrama de pachete este prezentată în figura următoare:
Figura 5.2.3.1.1 Diagrama de pachete
În continuare se va realiza o scurtă descriere a fiecărui pachet prezentat în figura anterioară.
Pachetul model
Acest pachet conține clasele model ale aplicației. O diagramă de clase a acestui pachet poate fi vizualizată în figura 5.2.3.1.2:
Figura 5.2.3.1.2 Diagrama de clase al pachetulului "model"
După cum se poate observa, clasele model au aceeași structură precum cele din cadrul aplicației server. Desigur, mai există anumite clase auxiliare precum "PhotoId", "FriendsChat", "ChatMessage".
Pachetul controller
Acest pachet conține la rândul acestuia mai multe subpachete precum: "adapters", "asynctasks", "broadcasts", "constants", "fragments", "http", "service", "utils". În continuare vom face o scurtă descriere a fiecărui subpachet.
Pachetul "adapters" conține adapter-ele folosite în aplicație. Fiecare clasă din acest pachet extinde clasa "RecyclerView.Adapter<NumeAdapter>". Un adapter este puntea de legătură dintre componentele grafice și datele care vin de la server.
Pachetul "asynctasks" conține toate task-urile asincrone executate în aplicație. Fiecare clasă din acest pachet extinde clasa "AsyncTask". AsyncTask permite o ușoară folosință a firelor de execuție(engl. thread) ale interfeței cu utilizatorul(engl. user interface – UI). Astfel, acesta permite executarea operațiilor în background și publicarea rezultatului la sfârșitul cererii fără a manipula firul de execuție al interfeței cu utilizatorul. Cererea HTTP este executată în mod asincron. Fiind derivate din clasa AsyncTask, clasele din acest pachet expun cele 4 metode de bază care realizează cererea în mod asincron:
onPreExecute() – conține acțiunile executate înaintea trimiterii cererii spre back-end;
doInBackground() – conține acțiunile executate în același timp cu procesarea cererii back-end;
onProgressUpdate() – conține acțiunile care arată statusul cererii;
onPostExecute() – conține acțiunile executate la finalul cererii, de obicei acestea interpretând rezultatul.
Figura 5.2.3.1.2 Diagrama de pachete din cadrul pachetului "controller"
Pachetul "broadcasts" conține partea de servicii specifice sistemului de operare Android implementate în cadrul aplicației. Au fost implementate 2 servicii: un serviciu care realizează mecanismul de logare în cadrul aplicației, respectiv un serviciu care realizează request-urile de tip "heartbeat". Clasele cele mai importante din acest pachet sunt: "AuthenticationServiceTask" și "HeartBeatServiceTask".
AuthenticationServiceTask realizează mecanismul de logare în cadrul aplicației. Această clasă extinde clasa IntentService și suprascrie metoda "onHandleIntent". În cadrul acestei metode se trimite un mesaj de tip broadcast către toate device-urile din acea rețea. După cum am explicat și în cadrul subcapitolului dedicat aplicației server principale, acest pachet conține un timestamp folosit pentru crearea token-ului de autorizare. Mesajul este trimis pe o adresă specifică broadcast ("230.0.0.1") și portul 2000. Aplicația așteaptă un răspuns din rețea pe durata maximă de 10 secunde. Procesarea mesajului primit din rețea este intepretat folosindu-se metoda "interpretResponse(String response, String token, String HOME_IP)". În cadrul acestei metode se efectuează diferite operații prezentate în figura 5.2.3.1.3.
Figura 5.2.3.1.3 Interpretarea răspunsului primit de la server
În funcție de răspunsul primit de la server, activitatea Main va afișa acest mesaj prin intermediul unui dialog.
HeartBeatServiceTask realizează mecanismul de "heartbeat" al aplicației. Aplicația server principală nu știe dacă utilizatorul este conectat în continuare la rețea. Pentru acest lucru, s-a folosit acest mecanism de "heartbeat" care execută la fiecare 5 secunde un HTTP request spre server care demonstrează faptul că dispozitivul este conectat în continuare la rețea.
Pachetul "constants" conține clase cu rol auxiliar în aplicație, precum clasele de tip Enum, dar și clase care conțin declarații statice ale unor valori default din cadrul aplicației.
Pachetul "fragments" conține fragmentele folosite în cadrul aplicației. Un fragment este o porțiune de interfață grafică folosită în cadrul unei activități. S-au folosit fragmente în cadrul acestei aplicații deoarece interfața grafică conține o componentă numită "navigation drawer". Folosind această componentă, dorim ca "toolbar-ul" aplicației să nu se modifice, indiferent de componenta vizuală care este afișată. Avem următoarele fragmente: "NewsFeedFragment", "MyProfileActivitiesFragment", "FriendsFragment", "ChatFriendsListFragment", "UpdateUserFragment", și "AboutUsFragment".
Fragmentul "NewsFeedFragment" afișează toate activitățile celorlalți utilizatori din aplicație. Tot în cadrul acestui fragment, utilizatorul are posibilitatea creării unei noi activități. Interfața grafică poate fi vizualizată în figura 5.2.3.1.3.
Figura 5.2.3.1.3 Fragmentul "NewsFeedFragment"
La încărcarea acestui fragment se execută un request HTTP către server prin care se solicită ultimele 10 activități create de ceilalți utilizatori. Aceste activități sunt salvate în adapterul "UserActivityAdapter" care le afișează sub formă de listă folosind o componentă grafică numită "RecyclerView". După cum am spus și în capitolulele anterioare, aplicația trebuie să fie cât mai rapidă iar încărcarea datelor să fie optimă. De aceea, atât pe partea de client, cât și pe partea de server s-au realizat paginări. Dacă utilizatorul face scroll iar ultima activitate(a zecea activitate) este vizualizată, se efectuează un alt HTTP request pentru solicitarea următoarelor 10 activități.
Pentru adăugarea unei fotografii se apasă butonul marcat printr-o cameră foto, iar utilizatorul are două posibilități: alegerea unei fotografii din galeria foto respectiv realizarea unei fotografii folosind camera dispozitivului. Figura 5.2.3.1.4 prezintă codul care pornește aplicația internă a camerei foto.
Figura 5.2.3.1.4 Startare aplicație cameră foto
Figura 5.2.3.1.5 prezintă codul care pornește aplicația pentru galeria foto.
Figura 5.2.3.1.5 Startare aplicație galerie foto
Odată ce fotografia a fost aleasă, se executa un request HTTP care salvează fotografia în baza de date. Dacă fotografia s-a salvat cu succes, se efectuează un al doilea request HTTP care salvează activitatea care conține fotografia în baza de date.
Fragmentul "MyProfileActivitiesFragment" afișează toate activitățile propriului utilizator. Interfața grafică poate fi vizualizată în figura următoare:
Figura 5.2.3.1.6 Fragmentul "MyProfileActivitiesFragment"
La fel ca și în cazul fragmentului "NewsFeedFragment", se execută un request HTTP către server prin care se solicită ultimele 10 activități ale utilizatorului curent. Acest lucru se realizează apelând un asynctask. Exemplu de cod poate fi observat în figura 5.2.3.1.6.
Figura 5.2.3.1.7 Apelare asynctask
Fragmentul "FriendsFragment" afișează toți utilizatorii existenți în aplicație. Interfața grafică poate fi vizualizată în figura următoare:
Figura 5.2.3.1.8 Fragmentul "FriendsFragment"
La fel ca și în cazul fragmentului "MyProfileActivitiesFragment", se execută un request HTTP către server prin care se solicită ultimii 10 utilizatori existenți în aplicație. Acest lucru se realizează apelând asynctask-ul "GetFriendsAsyncTask". Datele sunt încărcate în recyclerview cu ajutorul adapterului "FriendsAdapter".
Fragmentul "ChatFriendsListFragment" afișează lista de chat. Lista este afișată în interiorul unui recyclerview cu ajutorul adapterului "FriendsChatAdapter". Pentru a vizualiza lista de utilizatori, se face un request spre serverul de chat Openfire, prin care se încarca grupul de utilizatori existenți în aplicație. Următoarea secvență de cod aduce utilizatorii existenți in Openfire.
Figura 5.2.3.1.9 Încarcare utilizatori existenți în Openfire
Interfața grafică poate fi vizualizată în figura următoare.
Figura 5.2.3.1.10 Fragmentul "ChatFriendsListFragment"
Fragmentul "UpdateUserFragment" oferă posibilitatea utilizatorului de a-si edita informațiile personale. Utilizatorul poate să-și editeze fotografia de profil, fotografia de background, nume și prenume. Pentru modificarea fotografiei de profil / background requesturile care se execută sunt aceleași ca și în cazul fragmentului "NewsFeedFragment". Pentru modificarea informațiilor legate de nume, se apelează asynctask-ul "UpdateUserDetailAsyncTask". Interfața grafică poate fi vizualizată în figura următoare.
Figura 5.2.3.1.11 Fragmentul "UpdateUserFragment "
Fragmentul "AboutUsFragment" afișează o scurtă descriere despre aplicație precum și o serie de imagini surprinse în timpul utilizării acesteia.
Figura 5.2.3.1.12 Fragmentul "AboutUsFragment"
Pachetul "http" conține o clasă Singleton folosită pentru efecturea requesturilor HTTP către server-side. Conține toate metodele folosite în cadrul aplicației precum: GET, POST, PUT, DELETE.
Pachetul "service" conține clasele de business ale aplicației. Din cadrul acestui pachet se execută requesturile HTTP spre server-side. Tot în cadrul acestui pachet se află și clasele pentru conectarea aplicației la serverul de chat Openfire. Acest lucru se realizează în cadrul clasei MyXMPP. Modul de inițializare al conexiunii este prezentat în figura următoare.
Figura 5.2.3.1.13 Inițializare conexiune la Openfire
Pentru comunicarea cu serverul Openfire se folosește protocolul XMPP. Portul de comunicare este 5222. În cadrul acestei metode se setează un listener pentru a verifica dacă conexiunea la serverul de chat s-a realizat cu succes sau a apărut o eroare. Pentru recepționarea mesajelor se definește un listener care va intercepta mesajele de tip "chat". Clasa care realizează acest lucru este "MMessageListener" în cadrul metodei "processMessage".
Pachetul "util" conține clase auxiliare folosite pentru validări în cadrul aplicației.
Pachetul view
Acest pachet conține toate activitățile aplicației respectiv fragmentele custom create în cadrul aplicației. Aplicația conține 4 activități: "AuthenticationActivity", "MainActivity", "UserProfileActivity", "ChatActivity".
Prima activitate care rulează în cadrul aplicației este "AuthenticationActivity". În cadrul acestei activități se face autentificarea în aplicație, startând serviciul "AuthenticationServiceTask".
Figura 5.2.3.1.14 Startare serviciu autentificare
Tot în cadrul acestei activități se definește și un broadcast receiver care va afișa utilizatorului mesajul de confirmare cu privire la autentificare. Dacă logarea s-a realizat cu succes, tot în cadrul acestei activități se startează serviciul de "heartbeat" care va trimite câte un request HTTP la fiecare 5 secunde spre server-side. Interfața grafică poate fi vizualizată în figura următoare.
Figura 5.2.3.1.15 Activitatea "AuthenticationActivity"
Serviciul de heartbeat funcționează în background. Chiar dacă aplicația rulează în background sau nu rulează deloc, serviciul continuă să trimită requesturi HTTP de heartbeat către server-side.
Diagrama de clase din cadrul pachetului "view" poate fi vizualizată în figura următoare.
Figura 5.2.3.1.15 Diagrama de clase ale pachetului "view"
Activitatea "MainActivity" este activitatea principală a aplicației. În cadrul acesteia se afișează majoritatea informațiilor din aplicatie. Această activitate conține o componentă grafică numită "NavigationView". Această componentă adaugă un meniu prin care navigarea în cadrul aplicației se realizează mult mai ușor. Următoarea secvență de cod inițializează această componentă.
Figura 5.2.3.1.16 Inițializare componentă "NavigationView"
Pe lângă componenta "NavigationView", activitatea "MainActivity" conține și o componentă numită "FrameLayout". Această componentă va fi folosită pe post de cadru pentru fragmentele anterior prezentate. În mod default, această activitate va afișa fragmentul "NewsFeedFragment". În figura următoare este prezentat meniul aplicației.
Figura 5.2.3.1.17 NavigationView
Schimbarea fragmentelor se realizează folosind clasa FragmentManager. În figura următoare este evidențiată secvența de cod care realizează navigarea de la fragmentul curent la alt fragment.
Figura 5.2.3.1.16 Navigare între doua fragmente
Activitatea "ChatActivity" afișează discuția dintre doi utilizatori. Pentru trimiterea unui mesaj către un utilizator se apelează metoda "sendMessage(ChatMessage message)" din cadrul clasei "MyXMPP". O interfață grafică a acestei activități este prezentată în figura 5.2.3.1.17.
Figura 5.2.3.1.17 Activitatea "ChatActivity"
Activitatea "UserProfileActivity" prezintă profilul unui utilizator din cadrul aplicației. În cazul acestui fragment este posibilă vizualizarea activitățiilor acelui utilizator, efectuându-se un request HTTP care returnează ultimele 10 activități ale acelui utilizator. Ca și în cazul celorlalte fragmente, pentru vizualizarea mai multor informații, se efectuează multiple requesturi folosindu-se paginarea. Interfața grafică a acestei activități este prezentată în figura următoare.
Figura 5.2.3.1.18 Activitatea "UserProfileActivity"
Alte componente ale acestui pachet componentele custom precum "CustomDialogFragmentActivityComments", "CustomDialogPreviewPhoto", "CustomDialogRatedFriends", "CustomDialogUploadPhoto", "CustomLoadingProgessBar" etc.
5.2.3.2 AndroidManifest.xml
Fiecare componentă trebuie să declare Intent-ul căruia răspunde, adică odată cu ce Intent poate fi pornită. Declararea Intent-urilor se face în fișierul AndroidManifest.xml, unde fiecare componentă trebuie să declare acțiunea, categoria și tipul Intent-ului. În exemplul de mai jos este reprezentată declararea activității de autentificare, aceasta executându-se în momentul în care utilizatorul pornește aplicația. Din acest motiv, aceasta are setat ca acțiune MAIN, făcând parte din categoria LAUNCHER.
Figura 5.2.3.2.1 Declarare activitate în cadrul fișierului AndroidManifest.xml
Fișierul AndroidManifest.xml , pe lângă declarările de activități și Intent-uri, include și declarări de permisiuni. Pentru folosirea librăriilor specifice Internetului, sau a altor componente din telefon trebuie să se declare permisiunile specifice în acest fișier. Aplicația curentă folosește următoarele permisiuni:
Figura 5.2.3.2.1 Declare permisiuni în cadrul fișierului AndroidManifest.xml
5.3 Rularea aplicației server
Pentru rularea aplicației fără a folosi docker, se startează cele două aplicații de server respectiv serverul Openfire 4.0.2, în care aplicația server principală se startează cu profilul "local". Profilul "local" poate fi vizualizat în figura 5.3.1 .
Figura 5.3.1 Profilul "local" din fișierul "pom/xml"
Pentru rularea aplicației server în docker, calculatorul gazdă trebuie sa dețină un sistem de operare LINUX pe care să fie instalat ultima versiune de docker. Rularea aplicației server se efectuează startând scriptul "startApp.sh" cu drepturi de "root". Acest script face următorii pași:
startează containerul "mongodb" prin comanda: "docker run -d -p 27018:27017 –name mongodb mongo:2.2.7";
startează containerul "openfire" prin comanda: "docker run -d -p 9090:9090 -p 5222:5222 -p 7777:7777 –name openfire bunnyfu/openfire";
face build la aplicația server principală prin comanda: "mvn clean install -Pdocker -Dmaven.test.skip=true". Comanda de build folosește profilul "docker" declarat în fișierul "pom.xml" al aplicației principale de server. Profilul "docker" poate fi vizualizat în figura 5.3.2 .
Figura 5.3.2 Profilul "docker" din fișierul "pom.xml"
startează containerul "tomcat" prin comanda: "docker run -d -p 9080:8080 –name=tomcat –link=mongodb:mongodb –link=openfire:openfire tomcat-image";
startează aplicația server secundară prin comanda: "mvn clean install exec:java -Dexec.mainClass="org.stefanbalea.udp.sn.BroadcastMain" ".
Dacă startarea aplicațiilor server s-a realizat cu succes, terminalul sistemulului de operare ar trebui să arate precum în figura următoare:
Figura 5.3.3 Rezultatul executării scriptului "startApp.sh"
După rularea scriptului, interfața web a serverului Openfire poate fi accesată pe unul din porturile 9090 sau 9091. Se va realiza o scurtă configurare a acestui server, folosind baza de date "embedded" oferită ca suport de către Openfire. Ulterior, se vor instala plugin-urile necesare: "userService", "monitoring", "registration" din fereastra "Plugins". Se va explica în mod succint funcționalitatea fiecărui plugin.
userService – utilizat pentru un management mai bun al utilizatorilor existenți în aplicație. Operații de bază precum editare / ștergere utilizator sunt executate prin requesturi HTTP folosind caracteristicile acestui plugin.
monitoring – utilizat pentru arhivarea istoricului dintre doi utilizatori sau grup de utilizatori.
registration – folosit pentru adăugarea automată a noilor utilizatori într-un grup existent în aplicație, în cazul nostru fiind grupul "social".
În figura următoare este prezentată interfața grafică a serverului Openfire.
Figura 5.3.4 Interfața grafică Openfire
Capitolul 6
Testare și validare
6.1 Testarea aplicațiilor server
Testarea conexiunii cu baza de date s-a realizat atât în timpul implementării serviciilor cât și la finalul implementării. Testarea corectitudinii serviciilor s-a realizat prin implementarea testelor JUNIT. În cadrul capitolelor anterioare au fost prezentate diferite clase folosite strict pentru validarea datelor înainte ca acestea să fie introduse în baza de date. În acest fel se evită alterarea bazei de date.
În cadrul aplicației server principale s-au realizat aproximativ 140 de teste. Aceste teste oferă un "coverage" de aproximativ 90% pentru layerul "businessLogicLayer" respectiv 75% pentru layer-ul "dataAccessLayer". Pentru rularea testelor se folosește o bază de date numită "sn_test_db" distinctă față de cea folosită în cadrul aplicației, în acest fel testele nu pot fi influențate de eventualele informații deja existente în baza de date. Totodată, pentru rularea acestor teste sunt folosite fișiere de configurare distincte aflate în directorul "test". Structura folderului "resource" al proiectului de test este prezentat în figura următoare.
Figura 6.1.1 Structura folderului resource al proiectului test
După cum se poate observa în această figură, avem alt fișier de configurare pentru contextul Spring numit "applicationContext-test.xml".
La rularea aplicației pe "local" se rulează automat toate testele. În urma rulării testelor, rezultatul ar trebui să fie cel din figura 6.1.2.
Figura 6.1.2 Rularea testelor în cadrul aplicației server principale
O altă metodă de verificare al serviciilor create este utilizarea unui client de REST. Pentru verificare s-a folosit plugin-ul Postman.
6.2 Testarea aplicației Android
Aplicația Android a fost testată din privința utilizatorilor, prin simularea unui astfel de utilizator și verificarea comportării aplicației în diferite situații, printre care incluzându-se și cazurile de utilizare prezentate în secțiunea 5.1.
Deoarece în cadrul aplicației se trimit mesaje de tip broadcast, aplicația nu a putut testată folosind un Emulator. Din acest motiv, testarea aplicației s-a realizat pe dispozitivele fizice: Samsung GT N7000, Sony Xperia Z3 Compact, Huawei Nexus 6P, fiecare având versiuni diverse de sistem de operare. La momentul testarii, Samsung GT N7000 are versiunea "Android 4.2.2", Sony Xperia Z3 are versiunea "Android 5.1.1 Lollipop" iar Huawei Nexus 6P are versiunea "Android 6.0 Marshmallow".
În toate cele trei cazuri s-a urmărit modul de prezentare al interfeței grafice, răspunsul acesteia la comenzile utilizatorului, timpul de răspuns în cazul cererilor trimise spre server-side. În toate aceste cazuri testate, modul de prezentare al interfeței grafice nu a suferit modificari de la o versiuna de Android la alta, acesta rămânând identic. De asemenea, modul de funcționare a aplicației și răspunsul interfeței grafice la comenzile utilizatorului nu prezintă diferențe sesizabile. Singurele diferențe au apărut în cazul comunicării cu partea de server-side, existând mici diferențe de la o versiune la alta.
Pentru partea de comunicare cu server-side, testarea a constat în compararea datelor primite și afișate în aplicație cu cele existente în baza de date, dar și verificarea salvării datelor trimise din aplicație spre server-side.
În continuare vor fi prezentate câteva din validările existente în aplicația mobilă.
editarea informațiilor proprii – aplicația nu permite ca numele sau prenumele utilizatorului să conțină cifre sau caractere speciale.
Figura 6.2.3 Editare informații proprii
adăugarea unei noi activități – aplicația nu permite adăugarea unei activități care nu conține niciuna din cele două componente posibile ale unei activități: descriere sau fotografie.
Figura 6.2.4 Adăugarea unei noi activități
Capitolul 7
Concluzii
7.1 Concluzii
Obiectivul principal al acestui sistem este menținerea conexiunii dintre utilizatorii cu interese comune, fără folosirea prea multor resurse cum ar fi conexiunea la internet. În acest scop s-a încercat exploatarea tehnologiilor inovatoare apărute în ultimul deceniu, care au ca scop îmbunătățirea vieții umane. Proiectarea sistemului de față a inclus în componența acestuia o aplicație pentru dispozitive inteligente cu sistem de operare Android.
Cerințele și specificațiile proiectului au fost îndeplinite în totalitate, sistemul îndeplinind cu succes următoarele activități:
Logare în aplicație folosind broadcasting;
Modificarea informațiilor utilizatorului curent;
Adăugarea unei noi activități;
Ștergerea unei activități a utilizatorului curent;
Adăugarea unui comentariu la o activitate;
Ștergerea unui comentariu al utilizatorului curent;
Vizualizarea tuturor comentariilor unei activități;
Distribuirea unei activități pe peretele utilizatorului curent;
Efectuarea rating-ului unei activități;
Vizualizare listă cu utilizatorii care au efectuat rating;
Vizualizare listă de prieteni;
Vizualizarea profilului unui prieten;
Realizarea unui chat instant între doi utilizatori.
Sistemul este format din 3 componente majore: două componente server și o aplicație Android. Aceste componente au fost testate individual pe parcursul dezvoltării lor, ulterior fiind testată și integrarea acestora în ansamblul sistemului. Testarea s-a realizat atât manual prin definirea și executarea unor scenarii de utilizare cât și în mod automat prin scrierea unor teste JUNIT.
Așadar, proiectul este un sistem distribuit care permite comunicarea mai eficientă între utilizatori, singura condiție fiind deținerea unui telefon cu sistem de operare Android conectat la o rețea ad-hoc.
7.2 Dezvoltări ulterioare
Pentru o viitoare versiune a sistemului se pot aduce îmbunătățiri atât în ceea ce privește componentele server cât și componenta Android.
În ceea ce privește aplicația server principală, se pot aduce următoarele îmbunătățiri:
trimitere notificări în cadrul aplicației utilizând GCM(Google Cloud Messaging);
construirea unui plugin pentru serverul Openfire care trimite push notification atunci când aplicația este in background;
utilizarea unui agent pentru executarea task-urilor asincrone(ActiveMQ);
posibilitatea de adăugare a unui video în aplicație;
salvare fotografii / video în Cloud.
De asemenea, îmbunatățiri continue pot fi aduse și aplicației destinate dispozitivelor cu sistem de operare Android, în special în ceea ce privește interfața cu utilizatorul. La nivel funcțional, câteva exemple de îmbunătățiri sunt prezentate în continuare:
posibilitatea de vizionare a fișierelor video ale celorlalți utilizatori;
posibilitatea de a adăuga emoticoane în chat.
posibilitatea de a da distribui o activitate pe profilul altui utilizator.
Datorită domeniului pentru care este destinat, care este într-o continuă schimbare, îmbunătățtirea continuă a acestui sistem este necesară, printre actualizările obligatorii incluzându-se creșterea performanței și micșorarea timpilor de răspuns, sau un factor de utilizabilitate cât mai crescut în ceea ce privește interfața cu utilizatorul.
Bibliografie
[1] Arhitectura pe mai multe niveluri [Online].
https://en.wikipedia.org/wiki/Multitier_architecture;
[2] Arhitectura 3-tier [Online].
https://www.techopedia.com/definition/24649/three-tier-architecture;
[3] Avantaje si dezavantaje NoSQL [Online].
https://www.books-express.ro/blog/nosql-avantaje-si-dezavantaje/;
[4] Avantaje si dezavantaje MVC [Online].
http://www.careerride.com/MVC-disadvantages.aspx;
[5] Unit Testing cu JUnit [Online].
http://acs.ase.ro/Media/Default/documents/cts/curs/Curs%20CTS%20%20-%20JUnit.pdf;
[6] Diagrama Activity Lifecycle [Online].
https://developer.android.com/reference/android/app/Activity.html#ActivityLifecycle;
[7] Diagrama arhitectura Android [Online].
https://developer.android.com/images/system-architecture.jpg;
[8] Diagrama arhitectura MVC [Online].
https://www.formget.com/wp-content/uploads/2014/12/Controllers-Function-content-.png;
[9] Diagrama Arhitectura 3-tier [Online].
http://www.mrc-productivity.com/blog/wp-content/uploads/2011/01/nTierMobile1.jpg;
[10] Diagrama arhitectura Docker [Online].
https://docs.docker.com/engine/article-img/architecture.svg;
[11] Diagrama Service lifecycle [Online]
https://priyankacool10.files.wordpress.com/2014/04/service_lifecycle.png
[12] Database management system architecture [Online].
http://www.tutorialspoint.com/dbms/dbms_architecture.htm;
[13] Documentatie Android [Online].
https://developer.android.com/images/system-architecture.jpg;
[14] BooM (Bookmark Manager) [Online].
http://students.info.uaic.ro/~dordorut.calcai/BooM/Bookmark_manager.htm;
[15] Documentație Docker [Online].
https://docs.docker.com/engine/understanding-docker/;
[16] Documentație MongoDB [Online].
https://www.mongodb.com;
[17] Documentație Openfire [Online].
http://www.igniterealtime.org/projects/openfire/index.jsp;
[18] Documentație Spring [Online].
https://spring.io/;
[19] Documentație Quartz Scheduler [Online].
http://www.quartz-scheduler.org/;
[20] Documentație XMPP [Online].
https://xmpp.org/about;
[21] Hollister Sean, "Sony Smart Watch (aka Sony Ericsson LiveView 2) hands-on". The Verge, 2012.
[22] Injectarea de dependențe [Online].
andrei.clubcisco.ro/cursuri/5master/aac-cap/Curs13.doc
[23] Java APi for Restful Web Services [Online].
https://en.wikipedia.org/wiki/Java_API_for_RESTful_Web_Services;
[24] Laura June, "Toshiba AC100 Android smartbook hits the United Kingdom". Engadget, 2010.
[25] Mark Wilson, "T-Mobile G1: Full Details of the HTC Dream Android Phone". gizmodo.com, 2008.
[26] Noua generație de bază de date NoSQL [Online].
http://rria.ici.ro/ria2012_4/art04.php?lang=ro;
[27] Openfire [Online].
https://en.wikipedia.org/wiki/Openfire;
[28] Prezentare MVC [Online].
http://www.slideboom.com/presentations/959124/1.Modelul-MVC;
[29] Proiectarea aplicațiilor Java Enterprise folosind cadrul Spring 3.0 [Online].
http://thor.info.uaic.ro/~alaiba/mw/index.php?title=Proiectarea_aplica%C5%A3iilor_Java_Enterprise_folosind_cadrul_Spring_3.0;
[30] REST
https://en.wikipedia.org/wiki/Representational_state_transfer;
[31] Servicii REST [Online].
http://www.todaysoftmag.ro/article/81/restful-web-services-folosind-jersey;
[32] Sistemul de operare Android [Online].
https://ro.wikipedia.org/wiki/Android_(sistem_de_operare);
[33] Tutorial Docker [Online].
http://clneagu.ro/docker/;
[34] Testare unitara a claselor Java folosind JUnit [Online].
http://inf.ucv.ro/~mihaiug/courses/mps/labs/junit/;
Acronime
JSON – JavaScript Object Notation
API – Application Programming Interface
HTTP – HyperText Transfer Protocol
XML – EXtensible Markup Language
MVC – Model-View-Controller
FIFO – First In First Out
SDK – Software Development Kit
URI – Uniform Resource Identifier
REST – Representational State Transfer
TDD – Test Driven Development
NoSQL – Not Only SQL
BSON – Binary JSON
POJO – Plain Old Java Object
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: Crearea Unei Aplicatii In Doemniul Medical (ID: 113068)
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.
