Smart Presentation Feedback Comunicatia Client Server
Smart Presentation Feedback
Comunicația client-server
Rezumat
O prezentare ținută în fața unei audiențe numeroase reprezintă o activitate predominant unilaterală, în care cei prezenți în audiență nu pot interveni în niciun fel pe parcursul prezentării, astfel încât strângerea unui feedback relevant din partea acestora este dificilă. Acest lucru se întâmplă chiar și în condițiile în care majoritatea persoanelor dețin un dispozitiv smartphone sau chiar o tabletă, deci un suport electronic pe care ar putea urmări prezentarea și prin care s-ar putea devolta o interacțiune între aceștia și speaker.
Aplicația Smart Presentation oferă oportunitatea celor din audiență să urmărească prezentarea făcută de speaker pe propriul dispozitiv Android, smartphone sau tableta, în mod sincronizat cu prezentarea speakerului sau nu. În plus, aplicația oferă posibilitatea acordării de feedback direct pe documentul prezentării, în timp real, astfel încât speakerul să aducă lămuriri sau să răspundă la întrebări chiar în timpul prezentării, fără intervenția verbală a audientei. Speakerul are acces la forma agregată a feedbackului, extrem de util în cazul unei audiente mari.
Pentru realizarea acestei aplicații, am dezvoltat un sistem client-server, bazat pe cereri efectuate de clienți, dispozitivele Android, către un server web, care pune la dispoziție resurse ce pot fi accesate prin adresele lor URL. Resursele pot fi documentul prezentării sau feedback agregat adresat de cei din audiență.
ii
CUPRINS
Introducere
Contextul proiectului
Ideea și Scopul proiectului
Structura proiectului
Structura lucrării
2 Tehnologii folosite
Sistemul de operare Android
Tehnologiile specifice folosite la comunicarea client-server
Alte tehnologii folosite în cadrul proiectului
3 Arhitectura sistemului
Descrierea arhitecturii și modulele funcționale ale sistemului
Arhitectura clientului Android
Arhitectura serverului web
4 Detalii implementare
Accesarea resurselor pe baza URL și a protocolului HTTP
Structura URL
Tipurile mesajelor HTTP
Implementarea protocolului de comunicație
Componenta mesajelor și logica acestora
Serializarea datelor folosind Google Protocol Buffers
Implementarea clientului Android
Implementarea design patternului MVC
Persistența datelor în Sqlite și accesarea resurselor interne prin ContentProvider
Accesarea resurselor de pe web server prin mesaje asincrone
Implementarea serverului web
Inițializarea serverului și a containerului Grizzly
Clasele resursă și metodele HTTP definite pe server
Serializarea și deserializarea datelor
Modulul de clusterizare a întrebărilor și sincronizarea cu acesta
Interfața de utilizare a clienților Android
Interfațarea cu prezentarea, selecția elementelor
Metodele handler ale butoanelor
5 Utilizarea aplicației
Descrierea aplicației
Scenarii utilizare – screenshots 6 Concluzii
7 Bibliografie
iii
George-Cristian Stoica
1. Introducere
1.1 Contextul proiectului
Prezentările realizate în fața unei audiente de mărime medie sau mare pot fi uneori greu de urmărit din diferite motive, cum ar fi incapacitatea persoanelor din audiență de a înțelege anumite concepte din materialele prezentate, care duc la pierderea atenției, sau imposibilitatea acestora de a reveni asupra unor slide-uri anterioare.
Aceste prezentări ar putea deveni mai interactive, prin implicarea ascultătorilor încă din timpul audienței în procesul de apreciere pozitivă sau negativă a elementelor prezentate, precum și prin posibilitatea urmăririi prezentării atât în timp real cât și a revenirii asupra unor slideuri anterioare sau a avansării către slideuri următoare.
1.2 Ideea și scopul proiectului
Ideea proiectului Smart Presentation este de a realiza o interacțiune strânsă între membrii audienței unei prezentări și cel care ține prezentarea (asupra căruia mă voi referi în continuare ca speaker). 1
Ca precondiții, se presupune că atât speakerul, cât și cei din audiență posedă câte un dispozitiv mobil, tableta sau telefon mobil, având instalat sistemul de operare Android. De asemenea este necesar un server conectat la un router wireless, pentru a permite conectarea cu dispozitivele Android.
Funcționalitatea aplicației pornește de la posibilitatea membrilor audientei de a urmări pe propriul dispozitiv Android prezentarea ținută de speaker. Speakerul este cel care pornește prezentarea, care schimbă slide-urile și care termină prezentarea. Cei din audiență au posibilitatea de a urmări prezentarea în timp real și pe dispozitivul lor într-un mod live, sau pot naviga liber prin restul prezentării. În plus, aceștia pot furniza feedback în timp real prezentării astfel: pot selecta porțiuni din prezentare, asupra cărora pot furniza feedback pozitiv, de apreciere, feedback de ambiguitate, prin care se remarcă necesitatea unor clarificări ale acelor elemente sau feedback de cerere a unor dovezi (proof). De asemenea, cei din audiență pot pune întrebări legate de elementele selectate, sau pot alege să adere la o întrebare deja pusă, aceștia având posibilitatea de sincronizare a feedbackului cu cel acordat de toți cei aflați în audiență. În plus, aceștia vor avea și posibilitatea retragerii propriului feedback, dacă ulterior nu îl mai consideră necesar. La finalul prezentării, speakerul poate vedea o formă agregată pentru toate tipurile de feedback. În cazul întrebărilor adresate speakerului referitor la o selecție făcută pe prezentare, agregarea se face prin alegerea unor întrebări reprezentative din punct de vedere semantic și gruparea (eng. clustering) celorlalte întrebări în jurul acestora.
Comunicarea între dispozitive și sincronizarea elementelor de feedback acordate prezentării se face prin intermediul unui server central care depozitează datele agregate primite din partea tuturor clienților cu care se comunică wireless. Serverul este și cel care conține prezentarea în format PDF, aceasta fiind descărcată pe fiecare dispozitiv mobil care accesează aplicația SmartPresentation.
1.3 Structura proiectului
Proiectul a fost structurat în patru module cu funcționalități diferite, fiind cinci persoane implicate în dezvoltarea aplicației. Cele patru arii sunt:
1 Pagina web cu ideea proiectului, 20.06.2012, <http://aimas.cs.pub.ro/androidEDU/>
1
George-Cristian Stoica
modulul de manipulare a prezentării PDF. Acesta presupune folosirea unei biblioteci open source de manipulare a formatului PDF, pentru a permite navigarea prin prezentare, selectarea unor elemente ale prezentării prin folosirea ecranului touchscreen al dispozitivului Android și evidențierea selecției făcute prin diverse culori de highlighting.
interfața clientului pe Android, care include toate elementele vizuale prin care clientul interacționează cu aplicația (ferestre, butoane, liste).
modulul de comunicație client-sever, care asigură transmisia resurselor între dispozitive și server și sincronizarea acestora.
modulul de clusterizare a întrebărilor puse de audință pe baza similarităților semantice.
Partea mea de proiect a constat în implementarea modulului de comunicație între clienții Android și server. Acest modul include partea de stocare a datelor atât pe Android, cât și pe server, dezvoltarea serverului web și a protocolului de comunicație bazat pe HTTP și Google Protocol Buffers, obținerea resurselor prin URLuri, tipul mesajelor și modalitatea de serializare a acestora, precum și interfațarea pe server cu modulul de clusterizare a întrebărilor și interfațarea pe sistemul de operare Android cu modulul de manipulare a PDFurilor și cu interfața de utilizare.
1.4 Structura lucrării
În continuare, capitolele acestei lucrări sunt structurate astfel: aspecte teoretice ale proiectului, urmate de tehnologiile folosite la implementarea proiectului, cu detalierea celor folosite la comunicația client-server și la crearea protocolului de comunicație. Urmează arhitectura aplicației, în care sunt prezentate diferitele module ale aplicației și interacțiunea dintre ele, din nou cu detalierea celor de backend direct implicate în schimbul de date între clienți și server. Capitolul detaliilor de implementare prezintă metodele de programare folosite, oferind spre exemplificare porțiuni de cod explicate. În cadrul acestui capitol este descris protocolul de comunicație între server și clienți, tipurile de URLuri folosite pentru accesul resurselor, tipurile mesajelor schimbate și diferitele tipuri de serializare. Mai apare și detalierea interfațării dintre server și modulul de clusterizare a întrebărilor de feedback.
În final, capitolul de utilizarea a aplicației prezintă în detaliu aplicația și modalitatea de utilizare interfeței grafice, prin screenshoturi și înfățișarea unor diverse scenarii de utilizare.
2
George-Cristian Stoica
2. Tehnologii folosite
2.1 Sistemul de operare Android
Android este un sistem de operare pentru dispozitive mobile, telefoane sau tablete. Android a devenit lider mondial în acest segment în 2010, în primul trimestru al anului 2012 raportând o cotă de 59% din piața mondială a smartphoneurilor, cu un total aproximat la 331 milioane de dispozitive cu acest sistem de operare instalat1.
Android a început de la o companie mică, achiziționată de Google în 2005. În prezent, dezvoltarea sistemului de operare este supervizată de Open Handset Alliance, o comunitate condusă de Google din care mai fac parte companii că ARM Holdings, HTC, Inel, LG, Motorola, Samsung Electronics, Nvidia, T-Mobile sau Texas Instruments.
Kernelul sistemului de operare Android se bazează pe kernelul de Linux. Acest kernel a suferit modificări de arhitectura realizate de inginerii Google în afara procesului tipic de devoltare a kernelului Linux. Android nu are un sistem nativ X Window și nu suportă întregul set de biblioteci standard GNU, ceea ce face destul de dificilă portarea aplicațiilor și bibliotecilor existente de Linux pe Android. Principalele modificări pe care Google le-a adus într-o prima faza kernelului au fost legate de eficientizarea consumului bateriei. Aceste modificări au fost respinse în prima faza de dezvoltatorii kernelului Linux de bază, motivând lipsa de încredere în intenția Google de a se ocupa în continuare de acest cod. În 2010 s-a făcut un pas major pentru integrarea modificărilor din Android în Linux, prin adăugarea unui patch care îmbunătățea frameworkul de wakeup events existent și care permitea driverelor Android să fie ușor integrate în Linux de bază. În August 2011 Linus Torvalds spunea că în patru sau cinci ani Linux și Android se vor întoarce la un kernel comun [1].
Arhitectura Android presupune existența a patru layere, cel de la baza fiind kernelul de Linux. Al doilea layer, cel de middleware, conține biblioteci de C. Acesta este cod nativ, rulând direct peste cel de Kernel. Următorul layer este cel de application framework, care cuprinde biblioteci compatibile Java, bazate pe versiunea de Java Apache Harmony.
Fig. 1 Arhitectura Android 2
1 Wikipedia, The Free Enciclopedia, 20.06.2012,
< http://en.wikipedia.org/wiki/Android_(operating_system)>
2 Kebomix blog, 20.06.2012, <http://kebomix.wordpress.com/2010/08/17/android-system-architecture/>
3
George-Cristian Stoica
Mașina virtuală care face translația din codul Java în byte-code se numește Dalvik virtual machine, și se deosebește de mașina virtuală clasică JVM prin faptul că nu este o mașină bazată pe stivă, ci una bazată pe regiștri. Un tool numit dx este folosit pentru a converti o parte a fișierelor .class Java într-un format .dex. Mai multe clase sunt incluse într-un singur fișier .dex. Stringurile duplicate și alte constante folosite în mai multe fișiere class sunt incluse doar o dată în outputul .dex, pentru conservarea spațiului. Java bytecode-ul este de asemenea convertit într-un set de instrucțiuni definit de Dalvik Virtual Machine. Un fișier necomprimat .dex este sensibil mai mic decât o arhivă .jar, derivată din aceleași fișiere .class. O altă diferență majoră față de clasicele JVM este introducerea compilatoru, cel de la baza fiind kernelul de Linux. Al doilea layer, cel de middleware, conține biblioteci de C. Acesta este cod nativ, rulând direct peste cel de Kernel. Următorul layer este cel de application framework, care cuprinde biblioteci compatibile Java, bazate pe versiunea de Java Apache Harmony.
Fig. 1 Arhitectura Android 2
1 Wikipedia, The Free Enciclopedia, 20.06.2012,
< http://en.wikipedia.org/wiki/Android_(operating_system)>
2 Kebomix blog, 20.06.2012, <http://kebomix.wordpress.com/2010/08/17/android-system-architecture/>
3
George-Cristian Stoica
Mașina virtuală care face translația din codul Java în byte-code se numește Dalvik virtual machine, și se deosebește de mașina virtuală clasică JVM prin faptul că nu este o mașină bazată pe stivă, ci una bazată pe regiștri. Un tool numit dx este folosit pentru a converti o parte a fișierelor .class Java într-un format .dex. Mai multe clase sunt incluse într-un singur fișier .dex. Stringurile duplicate și alte constante folosite în mai multe fișiere class sunt incluse doar o dată în outputul .dex, pentru conservarea spațiului. Java bytecode-ul este de asemenea convertit într-un set de instrucțiuni definit de Dalvik Virtual Machine. Un fișier necomprimat .dex este sensibil mai mic decât o arhivă .jar, derivată din aceleași fișiere .class. O altă diferență majoră față de clasicele JVM este introducerea compilatorului JUST-IN-TIME, care reprezintă o metodă hibridă față de cele două metode clasice de runtime (intrepretat sau static – cod compilat). Astfel, acest compilator translatează codul din bytecode în machine code la runtime, înainte de a-l rula nativ.
Fiecare aplicație Android rulează în propriul proces cu propria instanță a mașinii virtuale. Dalvik a fost dezvoltat în așa fel încât un dispozitiv poate rula mai multe mașini virtuale eficient. Mașina virtuală Dalvik se bazează pe kernel-ul Linux pentru funcționalitățile de bază, cum ar fi gestionarea thread-urilor și menținerea nivelului de memoriei scăzut.
Layerul de application framework cuprinde acele servicii scrise în Java care fac legătura între aplicații și sistemul de operare, fiind separate pe diverse funcționalități: Activity Manager, Content Providers, Resource Manager, Notification Manager, View System, Telephony Manager, Location Manager și altele. Layerul de top este cel al aplicațiilor, unde dezvoltatorii pot adăuga noi aplicații utilizând API-ul existent sau pot modifica aplicațiile deja existente.
În prezent, Android are o vastă comunitate de dezvoltatori care scriu aplicații („apps”) care extind funcționalitatea dispozitivelor. Dezvoltatorii folosesc în principal codul versiunii custom de Java. Applicațiile pot fi descărcate de pe magazine online ca Google Play (fostul Android Market), magazinul condus de Google. În octombrie 2011 erau mai mult de 500 000 aplicații disponibile pentru Android.
În cadrul proiectului Smart Presentation, o mare parte din devoltare s-a făcut pe sistemul de operare Android, folosind versiunea 2.3 a sdk-ului Android. Dezvoltarea s-a făcut în Eclipse, acesta fiind mediul standard și global folosit pentru crearea aplicațiilor Android, datorită pluginului Android și a emulatorului care poate fi lansat direct din interfața IDEului.
În cadrul dezvoltării, am folosit atât cod nativ C, cât și cod standard Java. Codul nativ face parte din biblioteca mupdf, pe care am folosit-o la manipularea prezentării în format PDF, pentru partea de selecție. Codul nativ a fost compilat folosind toolul ndk, biblioteca rezultată putând fi încărcată direct în codul de Java.
Pentru modulul client-server, am folosit design patternul Model-View-Controller, detaliat la capitolul Arhitectura sistemului. Pentru implementarea acestui pattern am folosit clasele existente în API-ul Android, care încurajează separarea ariei funcționale, comunicarea pe retea sau persistența datelor, de interfața grafică a aplicației, pentru a se asigura un flow continuu al interfeței, fără întreruperi care ar dăuna calității utilizării. Principalele mecanisme folosite sunt cel de ContentProvider, care returnează activității de frontend conținut pe baza unor interogări (echivalent conceptului de Model). În cadrul ContentProviderului, persistența datelor este asigurată printr-o bază de date SQLlite, iar comunicarea cu serverul wireless se face asincron, în cadrul unor threaduri separate. Pentru rularea acestor taskuri, API-ul Android pune la dispoziție numeroase soluții, cea mai populară fiind cea de a extinde clasa AsyncTask, aceasta oferind metode handler pentru execuție și pentru returnarea rezultatelor.
4
George-Cristian Stoica
2.2 Tehnologiile specifice folosite pentru comunicarea client-server
Pentru crearea serverului central, responsabil cu centralizarea feedbackului de la clienți, distribuția acestuia către clienți și persistența datelor, am avut de ales între multiple tehnologii disponibile, cum ar fi realizarea unei comunicații asincrone pe socketi sau implementarea unui mecanism de Remote procedurce calling. Alegerea cea mai potrivită mi s-a părut în final implementarea unui web service, datorită protocolului de nivel superior, Hypertext transfer protocol, care asigură o bază pentru dezvoltarea unui mecanism facil de acces la date. De asemenea, acest protocol permite schimbul datelor în mai multe formate, atât de tip binar cât și plain text, prin completarea headerului de mime-type.
Un alt punct forte al alegerii unui sistem client-server bazat pe protocolul HTTP este portabilitatea, fiecare limbaj de programare având biblioteci care facilitează crearea mesajelor HTTP și transmiterea/recepționarea lor pe baza URLului. Datorită folosirii unui limbaj compatibil Java pe Android, eu am ales tot Java pentru devoltarea serverului web. Tehnologia Java pentru crearea web serverelor se bazează pe o arhitectură de tip servlet-container, care permite definirea unor scripturi Java (servlets) și înregistrarea acestora, de obicei într-un fișier de configurare xml, pentru a fi încărcate dinamic într-un container web. Un container web este acea componentă a unui serviciu web care interacționează cu servleturile și este responsabilă cu managementul ciclului de viață a acestora și cu maparea lor la un URL. Un web container implementează contractul unei componente web Java EE, specificând un mediu de runtime care asigură securitatea, concurența, managementul ciclurilor de viata, completarea tranzacțiilor și alte servicii.
În cazul acestei aplicații, pentru implementarea serverului web am ales să folosesc un web container Glassfish, bazat pe popularul Tomcat. Pentru interacțiunea cu acest container am folosit API-ul Grizzly, care va fi detaliat într-un subcapitol următor. Pentru maparea resurselor http unor metode http și a unor URLuri, am folosit frameworkul Java Jersey, o soluție care respectă arhitectura RESTful, cea mai folosită în momentul de față pentru dezvoltarea serviciilor web.
Protocolul HTTP
Hypertext Transfer Protocol, pe scurt HTTP, este un protocol la nivelul aplicație care stă la fundația comunicației în World Wide Web. Hypertext reprezintă un set de obiecte care construiesc conexiuni între noduri prin legături logice, hyperlinks. HTTP este protocolul care permite transferul unor astfel de obiecte1.
HTTP este un protocol de tip cerere-răspuns în modelul arhitectural client-server. Un client, cel mai des un web browser, trimite o cerere HTTP către un server web, care întoarce un răspuns. Acest răspuns conține o stare al completării cererii și, în caz de succes, informația cerută.
Resursele HTTP sunt identificate și localizate pe rețea prin Uniform Resource Locators (URLs) folosind schema http URI definită în RFC 3986 astfel:
<nume_schema> : <porțiunea_ierarhică> [ ? <query> ] [ # <fragment> ]
În cazul HTTP numele schemei este http. Partea ierarhică începe cu un dublu forward slash "//", urmat de un nume al autorității, care poate fi un nume de domeniu sau un ip, urmat apoi de un port opțional, precedat de ":". După autoritate urmează un path construit ca o succesiune de segmente, asemănător unei structuri de directoare, caracterul separator fiind "/". Porțiunea de query este opțională, începe cu "?" și conține informație adițională care nu este ierarhică, ci organizată tipic prin perechi de tip <cheie>=<valoare>, cu perechile separate prin ";" sau "&". Partea fragmentului este de
1 Wikipedia, The Free Enciclopedia, 20.06.2012, < http://en.wikipedia.org/wiki/Http>
5
George-Cristian Stoica
asemenea opțională, începe cu "#" și conține o directivă către o resursă secundară, cel mai des un atribut id al resursei principale, așa cum se întâmplă în cazul documentelor HTML.
HTTP definește nouă metode care indică acțiunea dorită asupra resursei accesate. Aceste nouă metode sunt: HEAD, GET, POST, PUȚ, DELETE, TRACE, OPTIONS, CONNECT și PATCH. Cele mai utilizate sunt cele de GET, prin care se citește resursa, și cele de POST, PUT și DELETE, prin care se modifică resursa. Metodele safe (sigure) sunt considerate cele prin care se intenționează doar citirea informației, fără modificarea stării serverului. Acestea sunt HEAD, GET, OPTIONS și TRACE. Metodele idempotente sunt cele asupra cărora mai multe cereri identice ar trebui să aibă același efect. Acestea sunt POST și DELETE.
O cerere HTTP conține următoarele :
o linie de request, spre exemplu GET /diverse/car.jpg HTTP/1.1 care formulează o cerere pentru resursa /diverse/car.jpg de la server.
Headere Http, cum ar fi Accept-Language: en-US, Accept-Charset: utf-8, etc.
O linie goală
Un corp al mesajului, opțional
Un răspuns HTTP conține următoarele:
linie de Status (spre exemplu HTTP/1.1 200 OK, care indică finalizarea cu succes a cererii clientului). Un status 3xx indică necesitatea unei redirectari, 4xx și 5xx sunt statusuri de eroare (4xx – eroare la client, 5xx- eroare la server).
Headere HTTP, cum ar fi Content-Type: text/html
O linie goală
Un corp al mesajului, opțional
Antetele HTTP sunt perechi "cheie: valoare", scrise în clear-text și separate prin succesiunea de caractere carriage return (CR) și line feed (FD). Aceste headere definesc parametrii de operare a unei tranzacții HTTP.
Datorită necesității trasmiterii unor tipuri diferite de conținut prin mesajele HTTP, am folosit în cadrul aplicației Smart Presentation setarea headerului Content-Type prin care se specifică mime-typeul conținutului. Un mime-type (Multipurpose Internet Mail Extension) este un standard Internet care a pornit de la descrierea seturilor de caractere folosite la formatul email, ajungând azi să fie utilizat ca standard de descriere al conținutului unui mesaj web în general. Un astfel de antet descrie atât conținuturi de tip text, cât și conținut binar. În cazul clienților și al serverului, interpretarea acestui câmp este vital pentru alegerea modului în care resursa este citită, respectiv scrisă.
Pentru aplicația Smart Presentation, am folosit următoarele tipuri mime:
text/plain – definește un conținut în text clar, necodat.
application/pdf – indică prezența unui fișier PDF în interiorul mesajului, în format binar
application/x-protobuf – indică un conținut binar, serializat cu Google Protocol Buffers. Este datoria clientului și a serverului de a serializa, respectiv deserializa acest format binar.
6
George-Cristian Stoica
Grizzly Web Container
Grizzly este un framework web născut din dorința de a ajuta dezvoltatorii să profite de avantajele API-ului Java NIO (Java new I/O). Este devoltat de comunitatea GlassFish, sub conducerea Oracle1. Până la apariția acestui API, scrierea unor aplicații server scalabile în Java era foarte dificilă, problemele de thread management făcând imposibilă scalarea unui server la mii de utilizatori. Scopul Grizzly este dezvoltarea unor sisteme server robuste și scalabile, oferind componente extinse ale frameworkului Java NIO ca:
un framework HTTP Protocol pentru crearea unor aplicații HTTP costumizate;
un framework HTTP Server care oferă abstractizări la nivel înalt ale protocolului HTTP (similare Servlets).
Grizzly folosește un sistem keep-alive bazat pe clasele Selector ale NIO, care suportă monitorizarea conexiunii și previn atacurile de tip Denial-of-Service. Sistemele Denial-of-Service adăugă suport pentru validare IP, numărul tranzacțiilor completate per IP, detecția conexiunilor inactive, cu scopul de a preveni epuizarea sau atacurile "flooding". Toate aceste servicii sunt folosite în conjuncție cu sistemele Keep-alive. Conectorul Grizzly va înainta cererile pentru resursele statice și dinamice către containerul servleturilor, care procesează cererile pentru resurse statice într-un servlet dedicat (org.apache.cătălina.servlets.DefaultServlet) [5].
Servicii Web RESTful și Jersey JAX-RS
Representational State Transfer (REST) reprezintă un stil arhitectural bazat pe standardele web și pe protocolul HTTP, pentru sisteme distribuite că World Wide Web. REST s-a evidențiat în ultimii ani ca modelul de design predominant al web-ului, datorită simplității sale. REST a fost descris în 2000 de Roy Fielding, în lucrarea sa de doctorat2.
Într-o arhitectură bazată pe REST, totul este o resursă. O resursă este accesată printr-o interfață comună bazată pe metodele standard HTTP. Într-o arhitectură REST, există tipic un server REST care asigură accesul la resurse și clienți REST care accesează sau modifică resursele. Fiecare resursă ar trebui să suporte operațiile standard HTTP (GET, POST, PUȚ, DELETE). Resursele sunt identificate prin ID-uri globale – URIuri.
"Limbajul" REST este bazat pe substantive și verbe și pune accentul pe lizibilitate. Spre deosebire de SOAP, REST nu necesită parsare XML și nu necesită un header al mesajului de la/către un service provider, ceea ce duce la reducerea bandwidth-ului consumat. REST are și o altă metodologie de manipulare a erorilor: în timp ce SOAP poate avea mesaje definite de eroare, REST necesită folosirea mecanismelor HTTP de error handling .
Stilul arhitectural REST impune anumite direcții de luat în considerare la realizarea designului, implementarea componentelor individuale fiind la discreția dezvoltatorului:
Client-server – o interfață uniformă separă clienții de servere. Separarea preocupărilor înseamnă că, spre exemplu, clienții nu se ocupă de stocarea datelor, așa cum serverul nu se ocupă cu interfața utilizatorilor sau cu starea clienților. Astfel, serverele și clienții pot fi dezvoltați independent, interfața rămânând constantă.
1 Grizzly website, 20.06.2012, < http://grizzly.java.net/>
2 Roy Fielding, "Architectural Styles and the Design of Network-based Software Architectures", 20.06.2012, <http://www.ics.uci.edu/~fielding/pubs/dissertation/top.htm>
7
George-Cristian Stoica
Stateless – acest principiu asigură că niciun context al clientului nu este memorat pe server între cereri. Fiecare cerere efectuată conține toate informațiile necesare, și orice informație de stare a sesiunii este stocată pe client. Aceasta este o caracteristică de rezistență la erori a serverelor.
Cacheable – clienții pot reține în cache răspunsurile. O memorie cache bine întreținută poate îmbunătăți scalabilitatea și performanța sistemului, prin reducerea numărul de cereri de la clienți la server.
Layered – un client nu-și poate da seama prin interfata comună dacă este conectat direct la server sau la un proxy intermediar. Servere intermediare pot îmbunătăți scalabilitatea sistemului prin load-balancing sau prin partajarea unei memorii cache.
Code on demand (opțional) – serverele pot să extindă temporar funcționalitatea clientului prin transferul codului – exemple fiind Java applets sau limbajele client-side scripting ca JavaScript.
Un WebService RESTful se bazează pe metodele HTTP și pe conceptele arhitecturii REST. Acesta definește tipic un URI de baza pentru resurse și tipurile MIME suportate (XML, Text, JSON, Protocol Buffers, etc) și setul de operații HTTP. Aceste metode standard sunt [6]:
GET – definește un acces la citirea unei resurse, fără efecte secundare. Resursa nu este niciodată alterată în urma unei cereri GET.
PUT – crează o nouă resursă, trebuie să fie idempotentă.
DELETE – șterge o resursă; operația trebuie să fie idempotentă, o repetare a cererii nu trebuie să producă efecte suplimentare primei cereri.
POST – actulizează resursa existentă sau crează o nouă resursă.
Jersey reprezintă implementarea Java open source, de referință, la calitate de producție a standardului JAX-RS (JSR 311) pentru dezvoltarea web serviceurilor RESTful. Dar este mai mult decât implementarea referinței, oferind și un api prin care programatorii pot extinde Jersey pentru nevoile proprii [7]:
Standardul JAX-RS implementat de Jersey suportă următoarele anotatii (annotations) pentru crearea resurselor web:
8
Google protocol-buffers
Protocolul HTTP este un protocol care permite definirea oricărui format de serializare a conținutului propriu-zis al pachetului, atât timp cât și emițătorii și receptorii definesc programatic metode de citire și scriere a acelor mesaje. Limbajul de facto folosit încă pentru schimbul de date este XML, datorită simplității sale și ușurinței de citire. Totuși, din dorința optimizării utilizării benzii de transfer , în dauna lizibilității, în cazul mesajelor mari sau a situaților în care există necesitatea schimbului unui număr foarte mare de mesaje, au câștigat teren și formatele de serializare binare [9].
Pentru serializarea mesajelor de mărime potențială mare, am folosit soluția Google Protocol Buffers, un mecanism extensibil, neutru din punct de vedere al limbajului și al platformei pentru serializarea binară a datelor, reprezentând o variantă mult mai eficientă față de XML și JSON. Acest mecanism a fost dezvoltat de Google pentru uz intern, aceștia creând compilatoare pentru C++, Java și Python disponibile publicului larg printr-o licență open source.
Designul Protocol Buffers pune accentul pe simplitate și performanță. A fost devoltat special pentru a crea o alternativa mai compactă și, deci, mai rapidă decât XML. Metoda de creare a mesajelor Protocol Buffers are la bază un limbaj de descriere a interfeței (IDL) care descrie structura datelor într-un format foarte simplu. Aceste "mesaje" sunt definite într-un fișier de definire Proto (.proto) care este compilat cu un executabil protoc. Compilarea generează cod care poate fi invocat de destinatarul sau receptorul acestor structuri de date. Clasele generate pun la dispoziția programatorului metode Get și Set pentru câmpurile mesajelor, asigurând citirea și manipularea facilă a acestora. De asemenea, se pot defini mesaje încapsulate în cadrul altor mesaje, care în mod programatic vor fi translatate în clase inner. O altă facilitate a claselor generate dinamic este posibilitatea obținerii unei instanțe a mesajului direct din streamul de octeți.
În mod canonic, Protocol Buffers sunt serializate într-un format binar care este compact, forward-compatible și backwards-compatible. Datorită simplității și a vitezei sale, acest format și-a extins popularitatea dincolo de comunicarea pe retea la cea între procese sau sisteme scrise în limbaje de programare diferite, care suportă serializare/deserializare Google Protocol Buffers.
message MessageExample
{
required int32 id = 1;
requird string messageName = 2; enum Type
{
TYPE1 = 1;
TYPE2 = 2;
}
9
George-Cristian Stoica
required Type type = 3; message EmbeddedMessage
{
required int64 id = 1;
required string label = 2 [default = "SAMPLE"];
}
repeated EmbeddedMessage internalLabels = 4;
}
Exemplu Protocol Buffers
După cum se poate observa, formatul .proto este foarte intuitiv, foarte facil de scris și permite o mapare la tipuri de date și structuri de date de bază în toate limbajele de programare populare: stringuri, tipuri numerice de date, liste, dar și concepte de programare orientată obiect, cum este încapsularea.
Astfel, eficiența acestui mecanism de serializare este dată atât de dimensiunea extreme de redusă a pachetului care este transferat pe retea, cât și de de modul foarte succinct și compact de definire a formatului mesajului. Aceste aspecte sunt net superioare limbajului XML, motiv pentru care foarte multe aplicații folosesc astăzi acest mecanism de serializare. Dezavantajul Google Protocol Buffers față de XML este acela că nu e human-readable, fiind un limbaj binar prea puțin important însă pentru aplicații care nu pun accentul pe urmărirea conținutului mesajului între procesele de serializare și deserializare.
Fig. 2 Comparație Xml – Google Protocol Buffers1
În cadrul proiectului Smart Presentation, am folosit Protocol Buffers la transmiterea între client și server a feedbackului acordat de ascultători sub forma de întrebări puse asupra unor elemente selectate din prezentare. Serverul este cel responsabil de serializarea clusterului de întrebări, mesajul proto fiind prezentat în detaliu la capitolul detaliilor de implementare.
1 Alternative to XML – Protocl Buffers. 20.06.2012, <http://afrozahmad.hubpages.com/hub/protocolbuffers>
10
George-Cristian Stoica
2.3 Alte tehnologii folosite în cadrul proiectului
Formatul PDF
Pentru reprezentarea documentelor am ales formatul PDF (Portable Document Format), motivul principal fiind reprezentat de popularitatea acestui și de unele avantaje tehnologice clare.
PostScript care este limbajul de programare interpretat aflat la baza formatului. Scopul său principal este de a descrie modul de randare a textului, formelor și imaginilor grafice. Acesta oferă, de asemenea, un cadru pentru controlul dispozitivelor de imprimare. Deși PostScript și PDF sunt înrudite, sunt formate diferite. PDF folosește capacitatea limbajului PostScript de randare de stiluri complexe de text și grafică și aduce această caracteristică atât pe ecran, cât și la imprimare. Pentru PDF s-a ales o flexibilitate redusă în favoarea unei eficiențe și unei predictibilități mai bune. Spre deosebire de PostScript, PDF poate conține o mulțime de structuri de document, link-uri, precum și alte informații conexe, dar nu poate schimba rezoluția, sau să folosească orice alte caracteristici specifice hardware.
Sintaxa PDF poate fi împărțită în patru părți [4]:
Obiecte. Un document PDF este o structură de date compusă dintr-un set redus de tipuri de obiecte. PDF include opt tipuri de baza de obiecte: valori boolene, numere întregi și reale, șiruri de caractere, nume, vectori, dicționare, fluxuri de date și obiectul null.
Structura fișierului. Structura fișierului PDF determină cum sunt stocate în fișier obiectele, cum sunt accesate și cum sunt modificate. Această structură este independentă de sintaxa obiectelor.
Structura fișierului PDF este formată din următoarele patru elemente:
Un antet format dintr-o singura linie specificând versiunea PDF.
Un corp conținând obiectele ce compun documentul.
Un tabel cross-reference conținând informații despre obiectele indirecte din fișier
Un trailer oferind locația tabelului cross-reference.
Structura iniațială poate fi modificată ulterior, ceea ce adăugă elemente adiționale la finalul fișierului.
Structura documentului. Structura documentelor PDF specifică modul cum obiectele de bază sunt folosite pentru reprezentarea componentelor unui document: pagini, fonturi, adnotări etc.
Catalogul documentului este un dicționar care conține referințe la alte obiecte care definesc conținutul, cuprinsul, threaduri de articole, nume de destinații și formulare interactive
Paginile documentului sunt acesate printr-o structură cunoscută ca arborele de pagini (eng. page tree), care definește ordonarea de pagini în document. Folosind această structură arborescentă aplicațiile de citit PDFuri care folosesc o cantitate limitată de memorie, pot deschide imediat un document conținând sute de pagini. Arborele conține noduri de două feluri: noduri interne reprezentate de arborii de pagini și frunze reprezentate de obiectele pagină.
Fluxuri de date. Un flux de date PDF conține o secvență de instrucțiuni care descriu modul de apariție al paginii sau alte entități grafice. Aceste instrucțiuni, deși sunt reprezentate că obiecte, sunt diferite din punct de vedere conceptual de obiectele folosite în descrierea structurii documentului.
11
George-Cristian Stoica
Datele dintr-un flux de date de conținut sunt interpretate ca o secvență de operatori și operanzii acestora. Un flux de date de conținut poate descrie modul de prezentare a unei poze sau poate fi tratat ca un element grafic în alte contexte.
Sistemul de coordonate definește spațiul în care randarea are loc. Determină poziția, orientarea și dimensiunea textului, graficelor și imaginilor ce apar pe pagină (Figura 3).
Conținutul unei pagini apar în cele din urmă pe un dispozitiv de ieșire raster, cum ar fi un ecran sau o imprimantă. Asemenea dispozitve variază în sistemul de coordinate folosit pentru a adresa pixeli. Sistemul particular al unui dispozitiv se numește spațiul dispozitv (eng. device space). Originea sistemului de coordinate al dispozitivelor poate fi în locuri diferite, de asemnea și axele pot avea orientări diferite.
Pentru a evita dependența de dispozitiv, PDF definește un sistem de coordonate independent de dispozitiv. Acest sistem de coordonate se numește spațiul utilizator (eng. user space).
Fig. 3 Relațiile dintre sistemele de coordonate (imagine preluată din ISO 32000-1)
Pe lângă aceste două sisteme de coordonate PDF mai folosește și alte spații de coordonate [4]:
Coordonatele pentru text va fi specificat în spațiul text. Transformarea din spațiu text în spațiu utilizator va fi definit de o matrice în combiantie cu alți parametrii specifici textului respectiv.
Simbolul unui caracter dintr-un font este definit în spațiul simbol (eng. glyph space). Transformarea din spațiul simbol în spațiul text va fi realizată de matricea de font.
Toate imaginile vor fi definite în spațiul imagine. Transformarea din spațiul imagine în spațiul utilizator este predefinit și nu poate fi modificat.
Biblioteca MuPDF
MuPDF este o bibliotecă software gratuită și open source dezvoltată în C care implementează un motor de parsare și randare de PDF-uri și XPS-uri. Acesta este utilizat în principal pentru a face pagini în bitmapuri, dar oferă de asemenea suport pentru alte operațiuni, cum ar fi căutarea, listarea cuprins și hyperlinkuri. Motorul de randare în MuPDF este adaptat pentru grafică de înaltă calitate. Aceasta randează textul cu o precizie de fracțiuni de pixel pentru o calitate cât mai bună în reproducerea aspectului unei pagini imprimate pe ecran.
Motivul pentru care a fost ales pentru acest proiect muPDF îl reprezintă caracteristicele de baza ale acestuia. MuPDF are o dimensiune redusă de cod, este rapid și, cu toate acestea, complet. Aceasta susține PDF 1.7, cu transparență, criptare, hyperlinkuri, adnotări, căutarea în text și mai multe. Suportă, de asemenea, și documente OpenXPS. Nu oferă suport pentru caracteristici interactive, cum ar fi
12
George-Cristian Stoica
completare de formulare sau JavaScript. Un alt avantaj pentru care am ales MuPDF este acela că este foarte bine modularizat, astfel încât alte caracteristici pot fi adăugate dacă se dorește acest lucru.
Există un număr de aplicații software gratuite care folosesc MuPDF pentru a randa documente PDF, cel mai important fiind Sumatra PDF. MuPDF este, de asemenea, disponibil ca pachet pentru Debian, Fedora, FreeBSD Ports și OpenBSD Ports.
MuPDF folosește o structură de date complexă pentru reprezentarea internă a documentului PDF, de care se folosește pentru a manipula fișierul pentru diferite funcționalități, precum randare a paginilor, extragere de text, de imagini, căutare în text etc. Practic realizează o copie în memorie a fișierului PDF aflat pe hard. Folosirea acestei structuri care e completată cu date efective din fișierul PDF facilitează realizarea de noi funcționalități, cum a fost și cazul acestui proiect.
13
George-Cristian Stoica
3. Arhitectura sistemului
3.1 Descrierea arhitecturii și modulele funcționale ale sistemului
Arhitectura sistemului aplicației Smart Presentation este de tip client-server, clienții fiind, din punct de vedere logic, de două tipuri: speakerul, cel care ține prezentarea și cei din audiență. Clienții au în general două posibilități: să trimită date către server, precum în cazul feedbackului, sau să interogheze serverul pentru resurse, spre exemplu documentul PDF, slideul curent al prezentării sau forma globală, agregată a feedbackului curent (Figura 4).
Serverul aplicației are rolul de a stoca resursele comune tuturor. Aceste resurse pot fi statice, cum este cazul documentului PDF, sau pot fi dinamice, cum e cazul elementelor de feedback sau a slide-ului curent al speakerului. În ambele cazuri, serverul are rolul de a centraliza datele și de a le pune la dispoziția clienților, prin adrese URL.
În cazul resurselor dinamince, serverul poate avea doar rolul de stocare, astfel încât orice client să poată accesa resursa, sau poate avea și un rol de prelucrare, cum ar fi în cazul elementelor de feedback. Aceste resurse sunt refăcute la primirea oricărui feedback, astfel încât răspunsul la o cerere să conțină o formă agregată a feedbackului.
Fig. 4 Arhitectura globală a sistemului
14
George-Cristian Stoica
3.2 Arhitectura clientului Android
Clientul Android implementează o arhitectură Model-View-Controller, care se caracterizează prin separarea clară a funcționalității celor trei module funcționale:
View – interfața grafică, alcătuită din totalitatea butoanelor, ferestrelor și elementelor cu care utilizatorul interacționează direct;
Controller – totalitatea handlerelor care sunt declanșate de utilizarea elementelor interfeței grafice. În cadrul acestor handlere, sunt formulate interogări asincrone către model sau către server pentru obținerea datelor.
Model – modulul care are rolul de persistenta a datelor și care poate face cereri pe rețea.
Interfața este actualizată prin returnarea răspunsurilor de la server, asincron. Aceste răspunsuri pot veni prin intermediul Modelului, dacă cererea este făcută intermediar la Model, în cazul aplicației Android acesta fiind implementat prin clasa ContentProvider. O altă variantă a actualizării vizuale este prin întoarcerea valorii direct prin execuția asincronă a unei cereri către server, caz în care controllerul este responsabil cu efectuarea cererilor către server.
O altă componenta de bază a modelului este baza de date, care este folosită pentru stocarea informațiilor de feedback, sau a informațiilor proprii clientului, cum ar fi date legate de propriul feedback. Aceste date se obțin prin interogarea content providerului, care poate decide dacă este nevoie de o actualizare a datelor prin cereri către server.
Mai jos se află o diagramă UML care descrie principalele componente ale Modelului, clasa content providerului și cele care deservesc bazei de date comunicării cu serverul (Figura 5):
Fig. 5 Diagrama UML a arhitecturii MVC implementată prin ContentProvider
15
George-Cristian Stoica
Arhitectura serverului web
Serverul central are două componente, care rulează pe threaduri separate:
un server web, care răspunde la cereri HTTP pentru resurse;
un modul de grupare (eng. clustering) pe baza similarităților semantice a întrebărilor de feedback. Acest modul comunică doar cu cealaltă componenta a serverului.
În cazul serverului web, există un thread principal cu un entry point din care se rulează serverul. În acest thread se inițializează pachetele claselor resursă, care sunt încărcate în background, comunicația facându-se prin crearea de threaduri secundare. Responsabil cu crearea și managementul acestor threaduri este containerul Grizzly.
Pentru stocarea datelor, am implementat o arhitectură de stocare ierarhică, formată din containere pentru diferitele tipuri de date (Figura 6).
Fig. 6 Diagrama UML a arhitecturii de stocare pe server
16
George-Cristian Stoica
Resursele HTTP care sunt disponibile la interogarea serverului web sunt implementate în clase specifice, care definesc metode programatice de interpretare a pachetelor HTTP și de alcătuire a răspunsurilor în cazul unor cereri (Figura 7).
În cazul resurselor dinamice, cum ar fi feedbackul de la utilizatori, serverul poate modifica resursa prin executarea unor operații interne, cum ar fi incrementarea numărului de feedbackuri pozitive asupra unei selecții (mapate la un URL) sau reclusterizarea întrebărilor pentru o selecție. În cazul al doilea, serverul procesează clusterizarea semantică într-un thread separat. Sincronizarea cu acesta se face printr-un lock și prin resurse de memorie comune, aflate în MemoryStore.
Fig. 7 Diagrama UML a claselor resursa UML și a metodelor de prelucrare
17
George-Cristian Stoica
4. Detalii de implementare
4.1 Accesarea resurselor pe baza URL și a protocolului HTTP
Aplicația SmartPresentation se bazează pe o arhitectură client-server, având în centru un server cu următoarele roluri:
stochează prezentarea pe care clienții cu dispozitive Android o descărcă la deschiderea aplicației;
menține evidența slideului curent al prezentării la care se află speakerul, oferind posibilitatea clienților de a-și sincroniza propria copie a prezentării cu cea a speakerului, prin folosirea modului "Go live!";
centralizează feedbackului primit de la clienți, stocând toate tipurile de feedback;
răspunde la cereri de la clienți pentru feedbackul agregat;
rulează modulul de grupare pe baza semantică a întrebărilor puse de cei din audiență și serializează structurile de date procesate de acest modul.
Pentru implementarea serverului am ales o soluție de web service, datorită existenței unor soluții multiple de frameworkuri astfel de servicii și datorită simplității accesării resurselor, prin Uniform Resource Locator.
4.1.1 Structura URLului
Containerul Grizzly folosit pentru stocarea resurselor http oferă posibilitatea setării la rulare a unui URL de bază, la care serverul web poate fi accesat. În cazul de fata, URL-ul de baza al serverului este format dintr-un IP și un port. În cazul stației pe care rulează serverul, acest URL are forma http://localhost:9998/, numele “localhost” fiind translatat local in ip-ul 127.0.0.1. În cazul clienților, aceștia vor avea posibilitatea accesării serverului prin ip-ul public al acestuia, care se setează indepenent de server la rularea acestuia, în funcție de setările ruterului wireless care asigură comunicația.
API-ul Jersey permite crearea unor clase resursă http, pentru care se definește prin adnotarea @Path porțiunea de URL care se adăugă URL-ului de baza al serverului. O adnotare de tipul @Path("/myresource") aplicată în cazul unei clase va face disponibilă resursa http la adresa http://localhost:9998/myresource, în cazul accesului de pe mașina locală care rulează serverul, spre exemplu dintr-un browser rulat local.
Pentru aplicația Smart Presentation, am definit mai mult tipuri de URL-uri, în funcție de resursele accesate. Acestea sunt prezentate în continuare prin porțiunea caracteristică resursei, care se adăugă adresei de bază:
/presentation : această adresa este accesată la pornirea aplicației Smart Presentation pentru descărcarea prezentării PDF stocate pe server;
/containers/slidecontainer/slide – adresa la care este reținut slideul curent la care se află prezentatorul. Prin accesul la această resursă, utilizatorii pot sincroniza prezentarea cu cea a speakerului. Speakerul actualizează această resursa automat la schimbarea slideului;
/containers/positivefeedback/[selection_id] – aceasta este resursa la care se reține numărul de feedbackuri pozitive care au fost acordate pe o selecție din prezentare, reprezentată de un id, număr întreg, al selecției;
18
George-Cristian Stoica
/containers/ambiguousfeedback/[selection_id] – aceasta este resursa la care se reține numărul de feedbackuri de tip ambiguu care au fost acordate pe o selecție din prezentare;
/containers/prooffeedback/[selection_id] – aceasta este resursa la care se reține numărul de feedbackuri de tip proof/citation needed care au fost acordate pe o selecție din prezentare;
/containers/questions/[selection_id] – aceasta este resursa la care se găsește forma agregată binar a întrebărilor puse pe selecție, grupate în clustere (liste) de întrebări similare din punct de vede semantic;
Pe lângă cele de mai sus, am mai definit patru resurse care sunt create și actualizate de server, la primirea oricărui tip de feedback. Acestea sunt cele de feedback agregat pentru toată prezentarea, necesare speakerului la finalul prezentării, și au următoarele adrese URL:
/containers/positivefeedback/aggregate – aceasta este resursa la care se reține o listă de mapari selecție – feedback pozitiv;
/containers/ambiguousfeedback/aggregate – aceasta este resursa la care se reține o listă de mapari selecție – feedback de tip ambiguous;
/containers/prooffeedback/aggregate – aceasta este resursa la care se reține o listă de mapari selecție – feedback de tip proof needed;
/containers/questions/aggregate – aceasta este resursa care stochează toate întrebările puse pe documentul PDF, prin gruparea acestora în selecții, fiecare selecție fiind un binar de tipul celei găsită la o resursă /containers/prooffeedback/[selection_id].
4.1.2 Tipurile mesajelor HTTP
Pentru transmiterea diferitelor mesaje HTTP între client și server, am folosit setarea antetului Content-Type al mesajului http. Următoarele tipuri au fost folosite pentru definirea conținutului:
text/plain: prin această setare am definit conținutul de tip clear text, în cazul mesajelor scurte, care nu necesitau serializare pentru o optimizare a comunicării. Astfel de mesaje sunt:
schimbarea slideului de către speaker respectiv accesarea slideului curent de către client, în ambele cazuri se transmite doar un număr al slideului;
transmiterea unui șir fix de două caractere, "+1" sau "-1", cu semnificația adăugării său retragerii unui feedback te țip fix, adică unul din tipurile positive feedback, ambiguous sau proof needed în cazul unei selecții. Selecția este reprezentată în URL prin id-ul ei, un număr întreg de tip long care se obține ca hashCode al unui șir de caractere reprezentativ pentru elementele selectate;
transmiterea ca răspuns de la server către client a unui șir de caractere reprezentând numărul total de feedbackuri din fiecare tip din cele trei descrise mai sus, pentru o anumită selecție. Acest număr este reprezentativ pentru agregarea tipurilor fixe de feedback, frecvența indicând relevanța acelui feedback;
application/pdf: această setare e folosită pentru codificarea binară a prezentării pdf stocate pe server. Prin interpretarea corectă acestui mesaj http, clientul preia conținutul binar și îl scrie într-un fișier de tip pdf, stocat local pe dispozitivul Android.
application/x-protobuf: acesta este un mime-type definit local corespunzător mesajelor http codificate binar folosind Google Protocol Buffers. Setarea conținutului binar al fișierului se poate face în mai multe moduri: în cazul clientului Android am folosit clasa ByteArrayEntity, care se instantiaza cu un content-type dintr-un vector de octeți, byte[]. În cazul serverului, sunt definite clase care implementează interfețele MessageBodyWriter și MessageBodyReader din pachetul
19
George-Cristian Stoica
jersey javax.ws.rs.ext, necesare pentru construirea unei clase Response pentru un mime type definit de utilizator. Pentru deserializare am folosit și metoda parseFrom() aplicată unui obiect Google Protocol Buffer, prin care se construiește instanța din vectorul de octeți al conținutului binar.
Setarea câmpului content-type și citirea acesuia se poate face foarte ușor programatic, în funcție de pachetele și clasele Java folosite pentru formarea și interpretarea mesajelor http. Astfel, pentru citirea câmpului, pe server se poate inspecta câmpul headers de tip HttpHeaders accesibil într-o metodă de tip Http a unei clase resursă, prin apelarea metodei header.getMediaType(). În cazul clientului, pentru citirea antetului se apelează metoda getFirstHeader("Content-Type") pe o instanță de tip HttpResponse. Pentru setarea acestui câmp, se poate instanția o entitate care implementează interfața HttpEntity cu tipul conținutului corect sau se poate seta acel câmp din antet, printr-o metodă setter.
4.2 Implementarea protocolului de comunicație
Un protocol de comunicație se definește ca un set de reguli cunoscute de ambele părți ale unei comunicații, prin care atât receptorul, cât și emițătorul pot interpreta corect mesajele transmise pentru îndeplinirea anumitor cerințe. Un protocol definește de asemenea și ordinea mesajelor schimbate, anumite operații necesitând schimbul unui număr consecutiv de mesaje specifice, într-o ordine particulară. Un protocol bazat pe HTTP, așa cum este cazul la aplicația Smart presentation, este format din două componente:
URL – atât clienții care accesează resursa cât și serverul pe care este stocată aceasta au aceeași concepție asupra resursei aflate la acea adresa. Astfel, clientul știe ce adresa trebuie accesată pentru obținerea resursei dorite și are certitudinea corectitudinii mesajului primit ca răspuns. Această certitudine permite interpretarea corectă a răspunsului. La fel, serverul asigură stocarea resursei cerute la respectiva adresă, având același set de mapări resursă-URL ca cel cunoscut de client.
Conținutul mesajelor – în cadrul unei resurse aflate la un URL, se încapsulează un conținut propriu-zis al mesajului care conține informația dorită. Această informație poate reprezenta date obținute prin prelucrare pe server sau pur și simplu stocate pe acesta. În funcție de conținutul acestor mesaje, un client poate decide transmiterea unor noi cereri sau declanșarea unor evenimente locale.
4.2.1 Componenta mesajelor și logica protocolului
În cadrul aplicației Smart Presentation, protocolul de comunicație este definit pe fiecare arie de funcționalitate separată. Astfel, serverul primește anumite cereri la care este pregătit să răspundă cu mesajele necesare. În continuare, mă voi referi la adresa serverului cu [adresa_server], datorită caracterului variabil al acesteia, în funcție de adresa ip la care poate fi accesat. De asemenea, mă voi referi la un URL cu termenul "adresa".
Mesajele schimbate între client și server sunt următoarele:
La inițializarea aplicației, fiecare client trimite o cerere http de tip GET la adresa [adresa_server]/presentation, cu conținut gol. Serverul trimite ca răspuns un mesaj http cu headerul Content-type setat "application/pdf" și conținutul binar, în format pdf, reprezentând aplicația stocată pe server.
La schimbarea slide-ului de către speaker, se trimite un mesaj de tip PUT către server, la adresa [adresa_server]/containers/slidecontainer/slide, având un conținut cu Content-type "text/plain" care este format dintr-un număr cu slide-ul curent. Clientul trimite cereri de tip GET la aceeași
20
George-Cristian Stoica
adresă, primind ca răspuns tot un mesaj http "text/plain" cu numărul slide-ului actual. Acest mesaj este trimis la un interval mic de timp, încontinuu, atunci când clientul se află în modul "Go live!".
Pentru trimiterea unui feedback de tip fix, positive feedback, ambiguous sau proof needed, clientul trimite un mesaj HTTP PUT cu conținutul "text/plain" reprezentat dintr-un șir de caractere cu forma "+1" sau "-1", cu semnificația adăugării sau retragerii acelui tip de feedback. Adresele de acces a acestor tipuri de feedback pentru o selecție sunt:
O [adresa_server]/containers/positivefeedback/[id_selecție] – pentru accesarea feedbackului de tip positive feedback
[adresa_server]/containers/ambiguousfeedback/[id_selecție] – pentru accesarea feedbackului de tip ambiguu
[adresa_server]/containers/prooffeedback/[id_selecție] – pentru accesarea feedbackului de tip proof needed.
La formularea unei cereri PUT la o astfel de resursă, serverul returnează același răspuns ca în cazul unei cereri GET, un mesaj de tip "text/plain" reprezentând numărul total al feedbackurilor de acel tip primite la acea adresa, adică pe o anumită selecție. Id-ul selecție este un șir de cifre care formează un număr întreg reprezentativ doar pentru o selecție.
Pentru mesajele ce conțin feedback de tip întrebări pentru o anumită selecție, am folosit codificarea în formatul binar Google Protocol Buffers, datorită simplității și versatilității sale în definirea mesajelor, dar și datorită unei eficientizări evidente a utilizării benzii de transfer. Ca și în cazul celorlalte tipuri de feedback, adresa la care este ținut feedbackul agregat pe un id al unei selecții este de forma [adresa_server]/containers/questions/[id_selecție]. Diferența apare la tipul conținutului unui mesaj și la logica răspunsului de la server în cazul transmiterii unei forme agregate a feedbackului stocat.
Astfel, un client trimite un feedback sub forma unui mesaj de tip HTTP PUT, cu câmpul content-type setat pe "application/x-protobuf". Conținutul acestui mesaj este un mesaj de tip protocol buffers, având la baza o întrebare adresată pe o selecție. La recepția acestei cereri, serverul reface resursa pe care o are reținută la acea adresă, prin regrupare. Această resursă conține o formă agregată a feedbackului, reținută în format binar Google Protocol Buffers. Conținutul acesui mesaj este format dintr-o listă de clustere, un cluster fiind o listă de întrebări cu sens asemănător din punct de vedere semantic. Acest mesaj este cel primit ca răspuns de un client sau de către speaker la o cerere de tip GET pe adresa corespunzătoare unei selecții.
De asemenea, am folosit Google Protocol Buffers pentru agregarea tuturor tipurilor de feedback pentru întregul document PDF, pentru ca acestea să poate fi accesate de speaker la final. Aceste resurse sunt găsite la adresele detaliate la capitolul anterior, de tipul:
[adresa_server]/containers/[feedback_container]/aggregate
Conținutul unui mesaj de tip Google Protocol Buffers poate varia, în funcție de tipul mesajului. Pentru a determina tipul mesajului înainte de deserializare, am folosit un mecanism de încapsulare care va fi descris în continuare.
21
George-Cristian Stoica
4.2.2 Serializarea datelor folosind Google Protocol Buffers
Mesajele de codificare a întrebărilor de feedback sunt serializate folosind mecanismul Google protocol Buffers, detaliat la capitolul Tehnologii folosite. În acest scop, am definit mai multe tipuri de mesaje într-o ordine ierarhică, în funcție de gradul de încapsulare.
Astfel, pentru o simplă întrebare, trimisă în cadrul unui mesaj sub forma unei cereri PUT de la client către server la o adresă specifică unei selecții, se folosește următorul format de mesaj:
message FeedbackQuestion
{
required int32 retract = 1; required string selectionString = 2; required string question = 3;
}
Pentru un cluster de întrebări, reprezentat printr-o listă de întrebări apropiate semantic, din care se poate alege oricare ca centroid (întrebare reprezentativă), am definit următorul mesaj:
message QuestionCluster
{
required string selectionString = 1; repeated FeedbackQuestion questions = 2;
}
Pentru reprezentarea agregată pentru o selecție a tuturor clusterelor obținute în urma aplicării algoritmului de grupare pentru toate întrebările adresate, am definit următorul mesaj:
message SelectionClusters
{
required int64 selectionId = 1; required string selectionString = 2; repeated QuestionCluster clusters = 3;
}
În plus față de aceste mesaje, am implementat mesaje de încapsulare pentru tipurile de feedback fix sau întrebări, pentru încapsularea mesajelor de agregare transmise speakerului la finalizarea prezentării.
message AggregateQuestionsFeedback
{
repeated SelectionClusters SelectionClusters = 1;
}
message FixedFeedback
{
required string selectionString = 1; required string feedbackType = 2; required int32 numberOf = 3;
}
message AggregateFixedFeedback
{
repeated FixedFeedback feedbacks = 1;
}
În cazul transmiterii unui mesaj Google Protocol Buffers, ar trebui cunoscut tipul mesajului înainte de deserializare, pentru folosirea claselor corecte generate de executabilul protoc. O tehnică potrivită
22
George-Cristian Stoica
pentru acest scop este încapsularea unui mesaj din cele trei tipuri de mai sus într-un mesaj container, care să conțină un câmp cu semnificația unui flag care indică tipul mesajului încapsulat, și un alt câmp cu mesajul propriu-zis. Formatul mesajului container arăta astfel:
message MessageContainer
{
enum Type
{
SINGLE = 1;
CLUSTER = 2;
SELECTION = 3; AGGREGATE_QUESTIONS = 4; AGGREGATE_FIXED = 5;
}
required Type type = 1;
optional FeedbackQuestion feedbackQuestion = 2; optional QuestionCluster questionCluster = 3; optional SelectionClusters selectionClusters = 4;
optional AggregateQuestionsFeedback aggregateQuestions = 5; optional AggregateFixedFeedback aggregateFixed = 6;
}
Astfel, câmpul type evidențiază care din cele trei mesaje opționale este cel conținut de mesajul container, permițând o deserializare corectă.
Pentru definirea acestor mesaje, am folosit următoarele cuvinte cheie ale limbajului Google Protocol Buffers, cu explicațiile:
Required – indică necesitatea prezenței acelui câmp în mesaj;
Opțional – indică lipsa necesității prezenței acelui câmp în mesaj;
Repeated – indică un câmp de tip lista al tipului specificat.
De asemenea, se poate observa și un tag care este asociat fiecărui câmp. Acest tag este utilizat la interpretarea mesajului la deserializare, indicând ordinea în care se găsesc membrii mesajului în formula binară a mesajului.
Generarea claselor, serializarea și deserializarea mesajelor
Google Protocol Buffers este un limbaj compatibil cu majoritatea limbajelor populare, oferind suport inclusiv pentru Java. Pentru obținerea mesajelor binare, este necesară obținerea unor clase Java din formatele definite în fișierele .proto. Generarea acestor clase face printr-un executabil protoc.exe. În cazul utilizării mediului de dezvoltare Eclipse, există un plugin care determină generarea automată a claselor la crearea unui mesaj .proto în folderul resources al unui proiect.
Clasele generate pun la dispoziție mai multe subclase și metode pentru serializare și deserializare. Pentru deserializare, cea mai facilă tehnică este apelarea metodei parseFrom() care primește că parametru un array de octeți (byte[]), spre exemplu conținutul unui mesaj HTTP. Pentru serializare, fiecare clasa conține o subclasa builder, care se obține astfel, pentru un exemplu din mesajele de mai sus:
SelectionClusters.Builder clusterBuilder = SelectionClusters.newBuilder()
Această instanță este folosită apoi pentru adăugarea celorlalte câmpuri, prin metode de tip setter;
23
George-Cristian Stoica
Exp: clusterBuilder.setSelectionId(id)
Pentru un câmp de tip repeated, se folosește metoda add().
După setarea tututor câmpurilor, mesajul se obține prin apelul metodei build asupra instanței builder. Exp: SelectionClusters clusters = clusterBuilder.build()
SelectionClusters.Builder scBuilder = SelectionClusters.newBuilder(); scBuilder.setSelectionId(new Long(MemoryStore.currentItem));
for (Set<Question> cluster : clusterTree)
{
QuestionCluster.Builder qCluster = QuestionCluster.newBuilder(); for (Question q : cluster)
{
FeedbackQuestion.Builder fqBuilder = FeedbackQuestion.newBuilder();
fqBuilder.setRetract(0);
fqBuilder.setQuestion(q.originalText); FeedbackQuestion fq = fqBuilder.build(); qCluster.addQuestions(fq);
}
QuestionCluster qc = qCluster.build(); scBuilder.addClusters(qc);
}
SelectionClusters sc = scBuilder.build();
Exemplu de cod din serializarea feedbackului
4.2 Implementarea clientului Android
Implementarea aplicației Smart Presentation include aplicația de client, cea de Android și serverul central. Implementarea clientului cuprinde cele trei module funcționale, interfața, modulul de manipulare a prezentării PDF și partea de comunicare cu serverul.
Mediul de dezvoltare Android este diferit de cel desktop. O diferență de baza între aplicațiile pentru telefoane mobile și aplicațiile desktop este reprezentată de modul în care tratează persistența datelor. Cel mai des, aplicațiile desktop folosesc o implementare a patternului MVC centrată pe documente – aplicațiile deschid un document, îl citesc în memorie și îl transpun în obiecte care persistă în memorie. Aceste programe definesc vizualizări în care pot fi manipulate acele obiecte ale modelului de date, prin procesarea inputului de la controller. Spre deosebire de acestea, aplicațiile Android combină modelele de date și interfața utilizatorului într-un mod diferit. Aplicațiile rulează pe un dispozitiv mobil cu memorie limitată, care poate rămâne fără baterie într-un moment imprevizibil. Întregul concept de document este absent în Android. Utilizatorul întotdeauna ar trebui să aibă datele la îndemână și să fie încrezător că acestea sunt în siguranță. Pentru a stoca datele aplicației incremental, obiect cu obiect, și ca acestea să fie întotdeauna la dispoziție, Android oferă un suport centrat pe baze de date, prin clasele cuprinse în pachetul android.database.sqlite [10].
O altă diferență între aplicațiile pentru telefoane mobile și aplicațiile desktop este accentul care cade în cazul celor mobile în primul rând pe o utilizare fluidă a aplicației, astfel încât interfața grafică să nu fie blocată niciodată. Din acest motiv, sdk-ul Android oferă multiple pachete și clase care permit
24
George-Cristian Stoica
rularea paralelă a mai multor taskuri, cu scopul de a nu încărca suplimentar threadul GUI. Spre exemplu, ultimele versiuni de Android sdk interzic rularea de operații de networking, cum sunt și cele http. Încercarea de rulare a unei astfel de cereri în threadul aplicației aruncă o excepție [2].
Cea mai des utilizată tehnica în devoltarea unor taskuri care rulează paralel este comunicarea asincronă cu threadul respectiv, astfel încât elementele vizuale, de interfață din threadul principal să fie înștiințate de finalizarea taskului paralel. Pentru implementarea acestei tehnici, am folosit două metode. Prima și cea mai simplă este de a implementa o clasă care extinde clasa AsyncTask, aceasta oferind metode pentru rularea în background și pentru finalizarea rulării. Cealaltă este cea de a realiza un managedQuery() către ContentProviderul aplicației, un serviciu care are rolul Modelului din arhitectura Model-View-Controller și care are acces direct la bază de date a aplicației. Ambele variante sunt detaliate în continuare.
4.3.1 Implementarea design patternului MVC
Așa cum am descris și la capitolul de Arhitectură, devoltarea aplicației Android implică implementarea unui pattern Model-View-Controller.
Astfel, se separă cele trei module funcționale, interfața grafică, modelul și controllerul, astfel încât fiecare componentă este dependentă de celelalte. Interfața grafică răspunde la comenzi din partea utlizatorului, controllerul fiind responsabil cu tratarea evenimentelor și cu actualizarea datelor din interfață. Modelul este cel care asigură persistența datelor din care atât controllerul, cât și view-ul își actualizează conținutul.
Și în cadrul aplicației Smart Presentation, există o separare clară a componentelor, astfel încât acestea pot fi încadrate într-una din cele trei categorii: view, model și controller. Astfel, view-ul este constituit din totalitatea elementelor grafice ale aplicației și controllerul cuprinde totalitatea handlerelor de actualizare a interfeței. Modelul este alcătuit din taskurile asincrone care comunică direct cu serverul web și dintr-o componentă specială, Content provider, care asigură persistența datelor într-o bază de date Sqlite și care poate fi interogată.
4.3.2 Persistența datelor în Sqlite și accesarea resurselor interne prin ContentProvider
Instanțele Content providers reprezintă o componentă a aplicației locale și sunt analoage conceptului de serviciu web RESTful. API-ul content providerului permite aplicațiilor client să interogheze sistemul de operare pentru date relevante utilizând un URI, similar modului în care un browser cere informații pe internet. Adresa URI a content providerului începe întotdeauna cu content://, la care se adăugă că în cazul unui web service componenta specifică resursei.
Dacă o aplicație implementează un provider propriu, este necesar ca acesta să fie înregistrat în fișierul de configurare al aplicației, AndroidManifest.xml, astfel:
<provider android:name=".provider.SmartpContentProvider"
android:authorities="smartp.client.provider.SmartpContentProvider" />
Utilizarea clasică a unui content provider se face prin stocarea datelor într-o bază de date SqLiteDatabase, asupra cărora acționează metodele implementate ale clasei abstracte ContentProvider:
Insert – este analoagă metodei POST a unui server web și are rolul de a stoca noi date în bază de date
25
George-Cristian Stoica
Query – analoagă metodei GET, returnează înregistrări din bază de date pe baza unor cereri SQL, într-o colecție specializată numită Cursor
Update – analoagă metodei update. Înlocuiește cererile din bază de date cu unele mai noi
Delete – analoagă cererii de tip DELETE
Ca și în cazul unui server web, un content provider mapează dinamic adrese URL la resursele aflate în baza de date. Această mapare este responsabilitatea programatorului, care la apelul metodei Query a content providerului face match pe URIul folosit în query pentru a ști ce interogare se realizează pe baza de date. Interogarea întoarce o instanță a clasei Cursor, care oferă pe lângă informația propriu-zisă mecanisme de declanșare a evenimentelor atunci când datele aflate la adresa respectivă suferă modificări. Pentru a activa aceste mecanisme, se apelează metoda [2]:
queryCursor.setNotificationUri(getContext().getContentResolver(),uri);
la crearea cursorului, adică în metoda Query a content providerului, și metoda :
getContext().getContentResolver().notifyChange(uri, null);
la modificarea conținutului, adică în interiorul unei metode insert, delete sau update.
În cazul ambelor metode, se folosește metoda getContext().getContentResolver(), care întoarce instanța unui ContentResolver, clasa care realizează interfața dintre aplicația care rulează și content providerul înregistrat în fișierul de configurare. Atât Context, cât și ContentResolver, sunt clase singleton, ale căror instanțe sunt create la inițializarea aplicației Android.
Clasa Content Provider a aplicației Smart Presentation are scopul de stocare în bază de date a unor date care sunt preluate de pe server. Cele mai importante astfel de date sunt documentul PDF, care este preluat asincron la deschiderea aplicației, după care aceasta este notificată pentru a-și reface interfața, și întrebările de feedback sub forma agregată, care sunt reținute asemănător unui mecanism de cacheing în bază de date, pentru a rări cererile la server. În ambele cazuri, din clasa activitate se folosește un query de tip managedQuery, care primește ca parametru adresa de interogare a content providerului și returnează un cursor cu datele din bază de date.
Clasa managedQuery are posibilitatea de mapare a unei instanțe a clasei ContentObserver, care implementează metoda onChange(), aceasta fiind metoda handler apelată atunci când un notify este executat pe URLul cursorului în cadul content providerului, cel mai uzual în cazul unui insert, delete sau update (Figura 8).
Fig 8. Componentele MVC de bază ale unei aplicații Android1
1 Zigurd Mednieks, Laird Dornin & G. Blake Mieke: Programming in Android 2nd Edition (O'Reilly, 2012), 80
26
Metoda query() a content providerului este responsabilă cu returnarea rezultatelor din bază de date. Acest matching se face folosind o clasă ajutătoare UriMatcher.
În cazul întrebărilor feedback reținute în baza de date, am decis să implementez o modalitate de cacheing, astfel încât în urma unei extrageri a intrării din baza de date corespunzătoare feedbackului unei selecții, să se verifice și timestampul ultimei actualizări, astfel încât o nouă cerere către serverul web să se facă doar atunci când resursele stocate sunt considerate prea vechi.
Extragerea căii de salvare a documentului PDF din baza de date se face doar o dată, la început, în urma unei cereri efectuate la serverul web care declanșează un notify la insertul în baza de date.
4.3.3 Accesarea resurselor de pe web server prin mesaje asincrone
Clasa AsyncTask
Accesarea resurselor de pe web server se face în două situații, din clasa de baza Activity sau din clasa ContentProvider, dar în ambele cazuri comunicarea cu serverul se face asincron. În cazul în care apelul se face direct din threadul UI, acest mecanism se implementează printr-o clasă care extinde clasa abstractă AsyncTask [2]. Această clasa oferă metode și parametri prin care comunică cu threadul apelant.
Cele trei tipuri utilizate de un task asincron sunt următoarele:
Params – tipul parametrilor trimiși taskului la execuție;
Progress – tipul unităților de progress publicate în timpul efectuării operațiilor în background;
Result – tipul rezultatului obținut în urma rulării operațiilor de background;
Aceste tipuri sunt setate atunci când se extinde clasa AsynTask, prin setarea celor trei parametri generici ai clasei. În cazul în care unul din cele trei tipuri nu este folosit, acesta poate fi marcat cu void. Un exemplu de declarare a unei clase copil ar fi:
private class MyTask extends AsyncTask<URL, Void, String)
care se traduce astfel: metoda de execuție primește ca parametri instante ale clasei URL, unitățile de progress nu sunt folosite, iar rezultatul este întors sub forma unui String.
Când un task asincron este executat acesta trece prin patru pași1:
onPreExecute(), invocat de threadul UI imediat după ce taskul este executat. Acest pas este utilizat în mod normal pentru setarea taskului, spre exemplu pentru afișarea unui progress bar în interfața utilizatorului.
doInBackground(Params …) invocat de threadul de background imediat ce onPreExecute() termină de executat. Acest pas este folosit pentru efectuarea unor acțiuni computaționale de background care pot dura un timp mai îndelungat. Rezultatul operațiilor efectuate aici trebuie să fie întors în această metodă și va fi pasat următorului pas. Acest pas poate declanșa și metoda publishProgress(Progress…) pentru a publica una sau mai multe unități de progres. Aceste valori sunt interpretate în UI thread, în pasul onProgressUpdate(Progres…)
onProgressUpdate(Progress…), invocat de UI thread după un apel al metodei publishProgress(Progress…). Momentul exact al executării este nedefinit. Această metodă este folosită pentru afișarea oricărei forme de progres în interfața utilizatorului în timp ce operațiile
1 Documentatia Android, 20.06.2012, <developer.android.com/reference/android/os/AsyncTask.html>
27
de background sunt în execuție. Spre exemplu, poate fi folosită pentru animarea unui progress bar sau pentru a afișa mesaje de log.
onPostExecute(Result), invocat de UI thread după ce operațiile de background se termină. Parametrul Result este cel preluat de metoda doInBackground().
Fig. 9 Fluxul de date AsyncTask1
O altă facilitate oferită de clasa AsynTask este posibilitatea opririi acesteia din threadul apelant, prin apelul metodei cancel(boolean). Invocarea acestei metode activează returnarea valorii onCancelled(Object) la terminarea metodei doInBackgroud(), în loc de onPostExecute. De asemenea, în timpul executării taskului se poate verifica periodic valoarea întoarsă de onCancelled().
Din punct de vedere al concurenței threadurilor, AsyncTask asigură că toate apelurile callback sunt sincronizate în așa fel încât următoarele operații sunt sigure:
setarea membrilor în constructor sau în onPreExecute() și referirea lor în doInBackground(Params…)
setarea câmpurilor membri în doInBackgroud(Params…) și referirea lor în onProgressUpdate(Progress…) și în onPostExecute(Result)
Aplicația Smart Presentation folosește numeroase clase particulare care implementează această clasă abstractă. În general, toate taskurile care presupun comunicare pe rețea, în acest caz efectuarea cererilor către serverul web și întoarcerea asincronă a răspunsurilor, sunt mapate la o astfel de clasă. Următoarele sunt principalele astfel de clase folosite în cadrul aplicației:
GetSlideAsyncTask – este clasa responsabilă cu sincronizarea slideului curent cu cel al speakerului, a cărei executare este lansată atunci când se intră în modul Go live!. Modul de funcționare a acesteia este următorul: funcția doInBackgroud(String… params) intră într-un ciclu în care, dacă nu a s-a apelat încă metoda cancel în UI Thread, se verifică ultima actualizare a slideului. Dacă timpul scurs de la ultima actualizare este mai mare de un anumit threshold mic (2-3 secunde), se execută o cerere asincronă către serverul web pentru actualizarea numărului slideului.
Verificarea dacă taskul continuă să ruleze se face prin apelul metodei isCancelled, care întoarce true sau false. După fiecare cerere GET efectuată, răspunsul este folosit pentru actualizarea
1 Zigurd Mednieks, Laird Dornin & G. Blake Mieke: Programming in Android 2nd Edition (O'Reilly, 2012), 110
28
interfeței grafice, în cazul nostru schimbarea slideului. Aceeași metodă de actualizare a slideului este apelată și în final în metoda onPostExecute(String result).
Metoda doInBackground(String… params) primește parametru de tip String, reprezentat de URLul la care se face cererea și returnează un răspuns de tip String, numărul slideului curent.
@Override
protected String doInBackground(String… params)
{
String response = ""; while (!isCancelled())
{
if (TimeUtils.getDiffTimeInSeconds(taskTimer) > 3)
{
taskTimer = TimeUtils.getCurrentTime(); HttpClient client = new DefaultHttpClient(); HttpGet httpGet = new HttpGet(params[0]); try
{
HttpResponse execute = client.execute(httpGet); response = HttpUtils.getStringFromResponse(execute);
changeSlide(response);
} catch (Exception e)
{
e.printStackTrace();
}
}
}
return response;
}
Implementare a metodei doInBackground()
Clasa PutCurrentSlide este folosită în aplicație doar de speaker, și este apelată atunci când acesta schimbă slide-ul. În cadrul metodei de background, se setează conținutul de tip "text/plain" al unei entități StringEntity, reprezentând slideul curent, și se execută o metodă de tip HttpPut.
Clasa PutFixedFeedback este folosită pentru trimiterea unuia din cele trei tipuri de feedback fix, positive feedback, ambiguous sau proof needed. Această clasa setează în constructor un membru String al clasei, numit PutOrRetract, cu valoarea "-1" sau "+1", cu semnificația adăugării acelui feedback sau retragerea unui feedback făcut anterior. Acest șir de două caractere este încapsulat în conținutul unei StringEntity și trimis printr-un http put către server, care returnează ca răspuns numărul agregat al acelui tip de feedback pus pe selecția respectivă. Afișarea răspunsului în aplicație se face la execuția onPostExecute()
Clasa PutFeedbackQuestionTask realizează serializarea unei întrebări de tip String și trimiterea acesteia către server. Serializarea se face în formatul Google Protocol Buffers și scrierea mesajului http se face folosind o entitate generică, ByteArrayEntity, care spre deosebire de StringEntity, folosește conținut de tip binar:
29
SmartPresentationMessage.Pdf.CONTENT_URI
ByteArrayEntity entity = new ByteArrayEntity(messsage.toByteArray());
entity.setContentType(smartp.client.AlternateMediaType.APPLICATION_XPROTOBUF);
Clasa UriRequestTask
Comunicarea cu serverul web se face nu doar din Activity, ci și prin intermediul Content Provider. În acest scop, am dezvoltat o clasă care implementează interfața Runnable, rulând într-un thread separat. La fel că în cazul AsyncTask, în cadrul metodei run() se execută operațiile de background. Pentru interpretarea mesajului, am implementat o clasă handler, MessageHandler, care parseaza mesajul http pentru a decide următorul pas. În general, datele cuprinse în răspunsul de la server sunt introduse în baza de date SQLite, astfel încât la o modificare a datelor, cursorul pe care s-a făcut o interogare pe bază de date să notifice elementele apelante din Activity.
Prima situație în care se întâmplă acest scenariu este la pornirea aplicației, când se execută un managedQuery la contentprovider pentru descărcarea prezentării PDF de la server.
public void query()
{
Uri queryUri = SmartPresentationMessage.Pdf.CONTENT_URÎ;
Cursor myCursor = managedQuery(queryUri, null, null, null, null); myCursor.registerContentObserver(new ContentObserver(new Handler()) {
@Override
public void onChange(boolean selfChange)
{
Log.d(SmartpTAG.TAG, "On load finished"); super.onChange(selfChange); onLoadPresentation(); initializeFrontend();
}
@Override
public boolean deliverSelfNotifications()
{
return true;
}
});
}
Metoda de interogare a content providerului
este adresa la care se face o interogare către
ContentProvider, și are forma content://" + AUTHORITY + "/" + Pdf.PATH, unde AUTHORITY este calea completă a clasei Content Provider și Pdf.PATH este o constanta care definește numele resursei. Această interogare declanșează cererea către web server. La descărcarea prezentării PDF, aceasta este scrisă în memoria internă a dispozitivului și adresa internă, cea specifică sistemul de fișiere Android este salvată în baza de date. În funcția care realizează insert în bază de date, se apelează metoda de notificare notifyChange pe URLul descris mai sus, astfel încât cursorul creat de metoda managedQuery
30
George-Cristian Stoica
să fie atenționat de terminarea cererii. În urma notificării, se execută metoda onChange, în care se încarcă prezentarea PDF în interfața grafică.
Aceeași succesiune de acțiuni ca și în cazul de mai sus se efectuează în cazul interogării ContentProviderului pentru obținerea feedbackului agregat. Aceste date sunt cerute prin intermediul instanței ContentProvider deoarece am dorit salvarea în bază de date a feedbackului, astfel încât să fie implementat un mecansim de cacheing care să permită aplicației să obțină feedbackul direct din baza de date dacă ultimul moment al actualizării sale a fost foarte recent. Acest feedback agregat este reținut în bază de date SqlLite în tabela QUESTIONS_TABLE, în cazul întrebărilor, și în FIXED_FEEDBACK, pentru celelalte tipuri de feedback. Interogarile pe tabele se fac după ID reprezentând selecția, și care conține un blob cu binarul serializat Google Protocol Buffers și un timestamp al ultimei actualizări.
4.4 Implementarea serverului web
Serverul central al aplicației Smart Presentation este un server web RESTful, care returnează resurse pe baza unor cereri efectuate pentru anumite adrese URL. Tehnologiile utilizate la dezvoltarea acestuia sunt Grizzly, pentru container, și Jersey, pentru implementarea claselor resursa și a metodelor specifice HTTP. Pe lângă componenta de resurse, serverul mai are și scopul de grupare a întrebărilor feedback de la clienți, pentru a permite utilizatorilor o vizualizare agregată, mai compactă, a întrebărilor, în funcție de similaritatea semantică. Astfel, partea mea de implementare a cuprins doi pași:
dezvoltarea serverului web și a claselor resursă și maparea acestora la adrese URL
integrarea modulului de clusterizare și sincronizarea acestuia cu resursa HTTP corespunzătoare feedbackului format din întrebări
Inițializarea serverului și a containerului Grizzly
Containerul Jersey oferă o interfață programatică de inițializare, spre deosebire de procedura standard de înregistrare a resurselor web specifice serverelor web Tomcat sau Glassfish, adică prin introducerea claselor într-un fișier de configurare web.xml.
Astfel, serverul poate fi pornit dintr-o metodă Main, permițând și operații anexe, cum ar fi în cazul aplicației Smart Presentation pornirea unui thread secundar al modului de clusterizare a întrebărilor feedback.
Inițializarea serverului web presupune următorii pași [5]:
Setarea unei adrese de bază a serverului web, spre exemplu http://localhost:9988/. Orice resursa stocată pe server are adresa formată din concatenarea acestei adrese unei porțiuni specifice resursei;
Înregistrarea pachetelor unde se află clasele resursa;
Crearea propriu-zisă a serverului web, prin apelul metodei:
GrizzlyWebContainerFactory.create(BASE_URI, initParams).
31
4.4.2 Clasele resursă și metodele HTTP definite pe server
Serverul web pune la dispoziție toate resursele de care clientul are nevoie pentru îndeplinirea funcționalităților aplicației Smart Presentation. Aceste resurse pot fi acccesate și modificate prin cereri HTTP de tip GET, PUT, POST sau DELETE. Clasele se mapează la adrese URL prin adnotări specfice frameworkului Jersey, care definește de asemenea și adnotări ale metodelor clasei pentru metodele HTTP la care acestea se mapeaza sau adnotări referitoare la tipul de content al corpului http.
Resursele expuse de serverul web sunt grupate în pachete care sunt înregistrate la inițializarea containerului Grizzly, pentru ca ele să poate fi accesate de class loader. Resursele specifice aplicației Smart Presentation sunt:
Class PDFPresentation – este clasa care pune la dispoziție prezentarea PDF stocată pe server. Aceasta este adnotată cu @Path("presentation"), indicând adresa la care poate fi accesată. Clasa implementează doar o metodă GET, adnotată cu @Produces("application/pdf"), indicând tipul conținutului pachetului http. În interiorul metodei, este citit într-un obiect File fișierul PDF aflat în directorul rădăcina al serverului . Acest obiect este încapsulat în răspunsul http prin metoda Response.ok((Object) file).
Clasa ContainersResource – adonatată cu @Path("containers"), este clasa care încapsulează totalitatea containerelor, întorcând un xml cu containerele definite.
Clasa ContainerResource – nu are adnotare de Path, șirul de caractere de după /containers/ fiind considerat un parametru care se parseaza la apelul metodei http. Metoda GET returnează răspuns doar dacă există acel container.
Clasa ItemResource – nu are adnotare de Path, numele său este șirul de caractere de după /containers/[container_name] și este instanțiată din metoda geItemResource a clasei ContainerResource. Are definite metode GET și PUT, pentru extragerea resursei, respectiv pentru crearea/actualizarea acesteia.
Ultimele trei clase descrise fac parte dintr-o arhitectură exemplificată în aplicația Storage Server definită ca exemplu al utilizării Jersey1. Acest design reprezintă o alternativă foarte generică de stocat resurse, întrucât ierarhia adresei permite separarea logică a acestora. De asemenea, resursele sunt reținute în format binar, din care se poate obține orice alt tip de date, în funcție de resursa stocată.
Din punct de vedere programatic, arhitectura Storage Server permite stocarea unor date care pot fi accesate în comun de resurse. Clasa care asigură stocarea se numește MemoryStore, și membrii clasei au atributul static, pentru ca aceștia să fie instantiati la încărcarea clasei și nu la o instantiere a sa. Excepție fac structurile de date de tip HashMap care realizează maparile containerelor și a numelor resurselor item la containere:
private Map<String, Container> containerMap = new HashMap<String, Container>(); private Map<String, Map<String, byte[]>> dataMap = new HashMap<String,
Map<String, byte[]>>();
Acestea sunt membri ale instanței singleton MemoryStore, care conține și metode accesor pentru aceste structuri de date.
În primul subcapitol al detaliilor de implementare am detaliat URL-urile la care pot fi accesate resursele serverului. Cu excepția documentului PDF și a clasei aferente, celelalte resurse sunt accesibile prin acest format al URLului:
1 Exemple Jersey, Storage-Service, 20.06.2012,
<http://docs.oracle.com/cd/E19776-01/820-4867/ggrby/index.html>
32
George-Cristian Stoica
[adresa_server/containers/[nume_container]/[nume_item]
Containerele sunt: slidecontainer, positivefeedback, ambiguousfeedback, prooffeedback și questions, cu semnificațiile prezentate anterior. Deși pachetele http întoarse ca răspuns pot avea formate diferite (plain/text sau Google protocol buffers), toate resursele sunt reținute în forma byte[], pentru a beneficia de genericitatea designului. Serializarea și deserializarea datelor din binar se face la nivelul clasei ItemResource, în funcție de numele containerului.
4.4.3 Serializarea și deserializarea datelor
Întrucât datele sunt reținute în binar sub forma unui vector de octeți în sistemul de stocare a serverului, este necesară deserializarea acestora atunci când se dorește interpretarea sau prelucrarea datelor, respectiv serializarea lor atunci când se introduc sau reintroduc datele în structurile de date de stocare.
În cazul resurselor de tip "text/plain", deserializarea se face ușor datorită constructorului String(byte[]). Serializarea este simetrică, prin metoda toByteArray() a clasei String.
În cazul întrebărilor de feedback și a celorlalte tipuri de feedback agregate, acestea sunt serializate și deserializate folosind metodele clasei Google Protocol Buffers generate de executabilul protoc la crearea mesajelor .proto. Deoarece la introducerea sau retragerea oricărei întrebări este necesară o regruparesemantică a întrebărilor, la efectuarea unei cereri PUT sau DELETE sunt necesari pași atât de serializare, cât și deserializare, deși în mod normal continiutul binar transmis în mesajul HTTP ar putea fi stocat direct. Acest lucru nu e posibil și din motivul că formatul mesajului primit de la client printr-o metodă PUT este diferit de cel reținut in binar pe server și transmis clientului la o cerere GET.
Serializarea mesajelor binare a fost detaliată la capitolul referitor la mesajele transmise între clienți și server. Acest proces se face prin clasa intermediară Builder a clasei respective mesajului Google Protocol Buffers. Deserializarea acestor mesaje se face cu metoda parseFromData(byte[]).
4.4.4 Modulul de grupare a întrebărilor și sincronizarea cu acesta
Pe lângă funcționalitatea de serviciu web, care răspunde la cereri http, serverul aplicației are și rolul de a rula gruparea întrebărilor de feedback de fiecare dată când o nouă întrebare este pusă de o persoană de audiență, deci o cerere PUT este efectuată pentru o întrebare pe o anumită selecție. Fluxul logic în acest caz este următorul:
utilizatorul formulează o întrebare pe dispozitivul Android, care este încapsulată într-un mesaj FeedbackQuestion (mesaj Google Protocol Buffers) și trimisa într-o cerere PUT la adresa selecției pentru care este adresată întrebarea
serverul web preia cererea, observă tipul cererii și este apelată o metodă a modului de clusterizare
modulul de clusterizare preia întrebarea ca input, citește binarul deja existent stocat în server (într-o formă deja agregată, în formatul SelectionsClusters), îl deserializeaza pentru a obține întrebările și realizează din nou clusterizarea, preluând și întrebarea de de input.
modulul de cluster serializează înapoi în formatul SelectionsClusters cu noile clustere de întrebări, apoi este refăcut mesajul container și binarul obținut este salvat în MemoryStorage.
Modulul de clusterizare a fost devoltat separat, astfel încât eu l-am integrat într-un thread separat, care este pornit odată cu serverul.
33
George-Cristian Stoica
Problemele evidente apar la sincronizarea între threadurile create în background pentru cererile web și threadul modulului de clustering. Pentru a rezolva această situație, am folosit un obiect simplu ca lock, acesta fiind vizibil tuturor claselor serverului ca membru al clasei MemoryStore. De asemenea, toate datele modificate la execuția clusterizarii sunt membri ai clasei MemoryStore. Toate aceste resurse accesate în comun sunt modificate într-o zonă sincronizată după MemoryStore.threadlLock [11].
Astfel, threadul de clusterizare se blochează în ciclul de rulare la apelul MemoryStore.threadlLock.wait(), urmând ca acesa să fie atenționat cu notify din threadul serverului web atunci când o nouă întrebare este adresată. În threadul cererii http, atunci când sosește o nouă întrebare, se intră într-o zonă sincronizată în care întâi se construiește întreg setul de întrebări, deserializându-se binarul deja reținut la care se adăugă noua întrebare, apoi se apelează notify pentru a reporni threadul clusterer. Acesta preia setul de întrebări din MemoryStore și reface clusterizarea, apoi serializează noile clustere într-un nou mesaj SelectionClusters, care este suprascris peste binarul stocat la URL-ul selecției. La sfârșit, acest thread iese din synchronized și repetă operația, blocându-se în wait.
public void run()
{
QuestionHierClusterer clusterer = new QuestionHierClusterer(); clusterer.init();
while (true)
{
clusterQuestions(clusterer);
}
}
public void clusterQuestions(, QuestionHierClusterer clusterer)
{
synchronized (MemoryStore.threadLock)
{
try {
MemoryStore.threadLock.wait();
Set<InputQuestion> input = clusterer.setInputQuestionsFromArray(MemoryStore.feedbackQuestions);
Set<Set<Question>> tree = clusterer.cluster(input);
storeClusters(clusterer, tree); } catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Exemplu cod de rulare al threadului de clustering
34
George-Cristian Stoica
4.5 Interfața de utilizare a clienților Android
Interfața aplicației oferă clientului posibilitatea utilizării întregii funcționalități a modului de comunicație client-server al aplicației Smart Presentation. Elementele vizuale permit utilizatorului să acorde oricare tip de feedback dintre cele suportate de server, unul din cele trei tipuri fixe sau feedback sub forma unei întrebări. De asemenea, clientul are posibilitatea sincronizării prezentării cu cea a speakerului, prin apăsarea butonului "Go live!". Odată intrat în acest mod, clientul poate ieși prin apăsarea oricărui buton care obligă păstrarea slideului curent, cum ar fi intenția de selecție a unor elemente din prezentare sau acordarea unui feedback.
Speakerul accesează serverul în mod indirect, prin schimbarea slideului, sau în mod direct, la sfârșit, când dorește vizualizarea întregului feedback. Scenariile de utilizare sunt detaliate la capitolul următor.
4.5.1 Interfațarea cu prezentarea, selecția elementelor
Pentru acordarea feedbackului pe o selecție a documentului PDF, trebuie întâi realizată selecția și reprezentată într-un mod în memorie. Selecția se declanșează la apăsarea unui buton, pentru a nu se încerca o selecție eronată, sau pentru necesitatea resetării selecției făcute.
Selecția se face prin alegerea a doua puncte din slide, cel de capăt stânga sus și cel de capăt dreapta jos. În momentul realizării selecției, este generat un șir de caractere reprezentativ pentru selecție. Acest script cuprinde numărul slide-ului și id-uri ale elementelor din slide astfel încât să se poată reconstitui selecția din șirul de caractere, necesară la prezentarea feedbackului pentru speaker.
Id-ul selecției folosit pentru accesarea și stocarea resurselor pe serverul web este numărul întreg obținut din aplicarea metodei hashCode() asupra stringului. Atunci când se realizează o selecție pe documentul PDF, se reține această mapare între string și hash-codul sau, pentru a fi posibilă operația inversă de determinare a șirului de caractere și, deci, a selecției vizuale din id-ul selecției.
4.5.2 Metodele handler ale butoanelor
Butoanele aplicației intră, la fel ca și ferestrele, la modulul de interfata Android. Legarea acestora de modulul de comunicație cu serverul presupune apelarea interogărilor atunci când un buton este apăsat. În clasa MuPDFActivity, clasa de startup a aplicației Smart Presentation, există definită o clasa handler comună pentru toate butoanele. Alegerea handlerului potrivit fiecărui buton se realizează în metoda OnClick, prin verificarea callerului [13].
Handlerele butoanelor aplicației pot efectua două tipuri de operații, din punct de vedere al comunicației și al accesării datelor: pot instanția o clasă AsyncTask și să apeleze execute pe aceasta, sau pot realiza o interogare managedQuery a Content Providerului, asupra căruia cade responsabilitatea efectuării cererii asincrone către server, dacă datele interogate nu sunt în baza de date sau sunt expirate (eng. ).
Butoanele care efectuează cereri directe care web server prin clase AsyncTask sunt cele care trimit feedback de tip fix către server. La fel este și cazul butonului "Go live!", care declanșează o acțiune repetitivă în cadrul unei clase AsyncTask, de sincronizare a slide-ului cu cel existent pe server, până la apăsarea unui alt buton care provoacă ieșirea din modul Live.
35
George-Cristian Stoica
5. Utilizarea aplicației
5.1 Descrierea aplicației
Aplicația Smart Presentation permite utilizatorilor unor dispozitive Android care iau parte la o prezentare să interacționeze în timp real cu aceasta, existând două moduri de utilizare: speaker și simplu ușer, persoana aflată în audiență. Scopul principal al aplicației este să permită celor din audiență să poată selecta elemente din slide-ul curent pe care să acorde diverse tipuri de feedback, cum ar fi: feedback pozitiv, în semn de apreciere, feedback de ambiguitate, care indică necesitatea unor clarificări suplimentare sau feedback de proof sau citation needed, pentru exprimarea necesității citării sursei. În plus, utilizatorii pot formula întrebări legate de selecția făcută adresate speakerului, sau pot vedea întrebările deja reținute de la alți ascultători pentru a-și exprima acordul cu una din ele. Întrebările sunt grupate semantic pe un server central, astfel încât toți userii să poate vedea doar o forma compactă a întrebărilor, fiind selectate doar cele reprezentative din punct de vedere semantic.
O altă facilitate a aplicației este posibilitatea de navigare liberă prin prezentarea descărcată pe propriul dispozitiv, sau folosirea modului live, care sincronizează permanent prezentarea cu cea a slide-ului speakerului până când modul este dezactivat.
La sfârșitul prezentării, speakerul poate vedea pe documente diversele feedbackuri primite din audiență pe timpul prezentării astfel încât să poată răspunde mai eficient la întrebări și să își îmbunătățească prezentarea pentru viitor.
5.2 Scenarii utilizare – screenshots
Pentru exemplificarea scenariilor de utilizare, vom presupune existența următoarelor persoane, cu următoarele roluri: Dan este prezentatorul, Alina, Andrei și Elena sunt simpli ascultători. Toți patru dețin un dispozitiv Android și documentul PDF este stocat pe un server aflat în apropiere care poate fi accesat prin mediul wireless.
În momentul pornirii aplicației, este declanșată o cerere către server de descărcare a documentului PDF stocat pe acesta. La primirea răspunsului și scrierea documentului în memorie, cei patru utilizatori sunt întâmpinați cu o fereastră care le permite să selecteze rolul pe care îl vor avea: speaker sau simplu ușer. Dan alege să fie speaker, ceilalți trei aleg să fie simpli ușeri. După alegerea rolului, cei patru își aleg numele cu care vor fi reținuți în sistem (Figura 10).
Fig. 10 Ferestrele inițiale ale aplicației
În următoarele situații sunt descrise activitățile userilor din audiență, adică Alina, Andrei și Elena. După alegerea numelui, este încărcată prezentarea de la început și aceștia pot vedea interfața aplicației (Figura 11).
36
George-Cristian Stoica
Fig. 11 Interfața clientului în aplicație
În continuare, Alina hotarește să parcurgă așa cum dorește prezentarea, pentru ca este curioasă de conținutul acesteia. Andrei și Elena hotaresc să fie sincronizați cu prezentarea ținută de Dan, pentru a fi mai atenți la ceea ce spune acesta. Astfel, cei doi apăsă primul buton din stânga jos, cel de Go live!, care le permite intrarea în modul sincronizat.
Pe parcursul prezentării, Alina hotareste să facă o selecție pentru a trimite un feedback de ambiguous. Pentru a face asta, ea apasă pe al doilea buton, cel marcat cu T, și realizează selecția. Dacă nu este mulțumită cu selecția făcută, ea poate reseta selecția prin apăsarea aceluiași buton.
Fig. 12 Selecție făcută pe o porțiune de text
În acest timp, Andrei și Elena se află la alt slide, cel la care se află și Dan, aceștia fiind în modul Live. Andrei se hotareste să pună o întrebare legată de titlul slideului. Pentru aceasta, el selectează întâi titlul (Figura 13).
37
George-Cristian Stoica
Fig. 13 Selecție făcută pe titlu
Apoi, acesta apăsa pe al treilea buton de jos, care îi arăta întrebările semnificative deja formulate pentru selecția făcută:
Fig. 14 Lista de intrebari
Andrei are posibilitatea de a alege o întrebare cu care să-si exprime acordul, sau de a alege elementul Add question din listă, care-i permite scrierea unei întrebări noi.
38
George-Cristian Stoica
Fig. 15 Adăugarea unei noi întrebări
Pe parcursul prezentării, Elena a acordat mai multe feedbackuri de tip ambiguous (penultimul buton) sau proof needed (ultimul buton), și a remarcat că, pentru unul din concepte, explicația se află deja în prezentare, motiv pentru care se întoarce la slide-ul respectiv și își retrage feedbackul pe aceeași selecție.
Fig. 15 Adăugarea unei noi întrebări
În timpul prezentării, Andrei, Elena și Alina acordă mai multe feedbackuri de tip +1, pentru a-și exprima o părere pozitivă legată de elementele selectate.
39
George-Cristian Stoica
La sfârșitul prezentării, Dan poate utiliza interfața proprie, care îi arată pentru fiecare tip de feedback selecțiile făcute pe document, pentru a asocia corect feedbackul cu elementele asupra cărora acesta a fost acordat.
40
George-Cristian Stoica
6. Concluzii
Dezvoltarea aplicației Smart Presentation a necesitat utilizarea multor cunoștințe practice și teoretice învățate în facultate, cum ar fi: protocoale de comunicații, programare și design orientate obiect, structuri de date, calcul paralel și sincronizare între threaduri, baze de date sau ingineria programelor. În plus, au fost necesare acumularea unor cunoștințe teoretice noi, cât și învățarea utilizării unor tehnologii noi.
Dezvoltarea serverului mi-a permis să studiez arhitectura RESTful pentru servicii web și să învăț să implementez un astfel de serviciu folosind framework-ul Jersey și utilizarea containerului Grizzly. Prin studierea diverselor exemple am ales să implementez un design ierarhic al claselor resursa http, și o clasă de stocare a datelor, accesibilă din toate resursele serverului. De asemenea, am folosit elemente de detaliu ale protocolului http, cum ar fi metodele specifice protocolului și headerul de content-type, care mi-a permis să trimit diferite tipuri de date în pachetele http.
Implementarea modulului suplimentar de grupare a ridicat probleme de sincronizare, datorită accesului comun la date din threaduri separate și a necesității creării unui mecanism de comunicare între threaduri.
Din punct de vedere al clientului, am învățat concepte importante de programare pe sistemul de operare Android. În primul rând, ideea de entry point sau clasa main sunt inexistente pe Android, unde serviciile și procesele lansate la execuția unei aplicații sunt definite într-un fișier de configurare. În al doilea rând, din punct de vedere al gândirii arhitecturale, programarea pe Android forțează utilizatorul să separe funcționalitățile aplicației după modelul arhitectural Model-View-Controller. Pentru respectarea acestui pattern, am folosit numeroasele clase oferite de sdk-ul Android care facilitează rularea paralelă a taskurilor și sincronizarea cu threadul principal. Stocarea datelor se face într-un mod canonic într-o bază de date Sqlite care asigură persistența datelor. În cazul meu, am folosit o astfel de bază de date pentru persistența unor informații, similar cu păstrarea unor structuri de date, dar într-un mod mai robust și care permite accesul simplu, prin adrese URI.
Pentru a realiza cererile HTTP de la client către server, am explorat versatilitatea clasei AsyncTask, prin care dezvoltatorului i se oferă toate mecanismele necesare sincronizării threadului apelant cu cel de background și actualizarea datelor și a interfeței cu cele incluse în răspunsul primit de la server.
Comunicația între client și server mi-a lăsat la alegere conceperea diverselor tipuri de mesaje, serializate în mod diferit și cu semnificații diferite, care alcătuiesc protocolul aplicației. Pentru mesajele largi, de agregare a feedbackului de întrebări, am ales să folosesc mecanismul de serializare binară Google Protocol Buffers, o soluție optimă și foarte ușor de implementat, datorită generării automate a claselor Java dintr-un format de definire a mesajelor proprietar, user-friendly.
Rezultatul final al lucrării s-a transpus într-o aplicație care îndeplinește obiectivele formulate inițial:
posibilitatea sincronizării prezentării unui client Android din audiență cu cea a speakerului;
acordarea de feedback de tip positive feedback, ambiguous, proof needed sau sub forma unei întrebări pe o selecție a unui slide sau pe întregul slide curent al prezentării;
vizualizarea feedbackului agregat în timp real și posibilitatea exprimării acordului cu feedback deja formulat de alte persoane;
retragerea feedbackului propriu efectuat;
interfețe diferite de utilizare pentru speaker și audienta;
vizualizarea de către speaker a unei variante finale, agregate a tuturor tipurilor de feedback acordate de audiență.
41
George-Cristian Stoica
În viitor, aplicația poate fi dezvoltată, pentru a permite diverse facilități noi. În primul rând, este necesară gruparea mai eficientă a întrebărilor, bazată pe un algoritm care să grupeze mai inteligent întrebările pe baza similarității semantice și care să aleagă un centroid al clusterelor reprezentativ pentru întrebările grupate. În prezent, gruparea se bazează în special pe cuvinte de bază și nu pe cuvinte cheie care pot schimba sensul unei întrebări.
Alte îmbunătățiri care pot fi aduse aplicației sunt: o interfata mai interactivă și informativă a aplicației, care să permită o vizualizare mai intuitivă și mai cuprinzătoare a feedbackului, devoltarea unui sistem de autentificare pentru a acorda privilegii diferite ușerilor, posibilitatea încărcării în realtime a unui document nou de prezentare sau dezvoltarea unei interfețe grafice a serverului, care să permită o configurare a acestuia.
De asemenea, ar fi interesant/util de implementat o clasificare a userilor, în funcție de o reputație a acestora, care să le acorde o pondere mai mare propriilor feedbackuri în fața unor utilizatori noi.
42
George-Cristian Stoica
7. Bibliografie
Wikipedia, Android Operating systems, 20.06.2012, <http://en.wikipedia.org/wiki/Android_%28operating_system%29>
Zigurd Mednieks, Laird Dornin, G. Blake Meije, Programming Android, 2nd Edition, O'Reilly, 2011
Documentația oficială Android, 20.06.2012, <http://developer.android.com>
International Organization for Standardization, ISO 32000-1:2008 Document management – Portable document format – Part 1: PDF 1.7, 20.06.2012, <http://www.adobe.com/content/dam/Adobe/en/devnet/acrobat/pdfs/PDF32000_2008.pdf>
[5] Website Grizzly, 20.06.2012, <http://grizzly.java.net>
[6] Sameer Tyagi, RESTful Web Services,
< http://www.oracle.com/technetwork/articles/javase/index-137171.html>
Documentația oficială Jersey, 20.06.2012, < http://jersey.java.net/nonav/documentation>
RESTful Web Services Developer's Guide, 20.06.2012,
<http://docs.oracle.com/cd/E19776-01/820-4867/ggrby/index.html>
Documentația oficială Google Protocol Buffers, 20.06.2012, <https://developers.google.com/protocol-buffers/>
Vogella tutorials, 20.06.2012, <http://www.vogella.com/articles/AndroidSQLite/article.html>
Scott Oaks, Henry Wong, Java Threads, 3rd Edition, O'Reilly, 2004
RESTful Web Services Developer's Guide, Chapter 5 – Jersey Sample Applications, 20.06.2012 <http://docs.oracle.com/cd/E19776-01/820-4867/ggrby/index.html>
Marko Gargenta, Learning Android – Building Applications for the Android Market , O'Reilly, 2011
43
. Bibliografie
Wikipedia, Android Operating systems, 20.06.2012, <http://en.wikipedia.org/wiki/Android_%28operating_system%29>
Zigurd Mednieks, Laird Dornin, G. Blake Meije, Programming Android, 2nd Edition, O'Reilly, 2011
Documentația oficială Android, 20.06.2012, <http://developer.android.com>
International Organization for Standardization, ISO 32000-1:2008 Document management – Portable document format – Part 1: PDF 1.7, 20.06.2012, <http://www.adobe.com/content/dam/Adobe/en/devnet/acrobat/pdfs/PDF32000_2008.pdf>
[5] Website Grizzly, 20.06.2012, <http://grizzly.java.net>
[6] Sameer Tyagi, RESTful Web Services,
< http://www.oracle.com/technetwork/articles/javase/index-137171.html>
Documentația oficială Jersey, 20.06.2012, < http://jersey.java.net/nonav/documentation>
RESTful Web Services Developer's Guide, 20.06.2012,
<http://docs.oracle.com/cd/E19776-01/820-4867/ggrby/index.html>
Documentația oficială Google Protocol Buffers, 20.06.2012, <https://developers.google.com/protocol-buffers/>
Vogella tutorials, 20.06.2012, <http://www.vogella.com/articles/AndroidSQLite/article.html>
Scott Oaks, Henry Wong, Java Threads, 3rd Edition, O'Reilly, 2004
RESTful Web Services Developer's Guide, Chapter 5 – Jersey Sample Applications, 20.06.2012 <http://docs.oracle.com/cd/E19776-01/820-4867/ggrby/index.html>
Marko Gargenta, Learning Android – Building Applications for the Android Market , O'Reilly, 2011
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: Smart Presentation Feedback Comunicatia Client Server (ID: 108067)
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.
