treasure huntalaiba/pub/absolvire/2016...cultura iașului” am propus o abordare proprie și...
TRANSCRIPT
1
Universitatea Alexandru Ioan Cuza Iași
FACULTATEA DE INFORMATICĂ
LUCRARE DE LICENŢĂ
Treasure hunt
propusă de
Adrian-Dumitru Munteanu
Sesiunea: Iulie, 2016
Coordonator ştiinţific
Asist. Dr. Vasile Alaiba
2
UNIVERSITATEA ALEXANDRU IOAN CUZA IAŞI
FACULTATEA DE INFORMATICĂ
Treasure hunt: aplicație web
ce promovează cultura Iașului
Adrian-Dumitru Munteanu
Sesiunea: Iulie, 2016
Coordonator ştiinţific
Asist. Dr. Vasile Alaiba
3
DECLARAŢIE PRIVIND ORIGINALITATE ŞI RESPECTAREA DREPTURILOR
DE AUTOR
Prin prezenta declar că Lucrarea de licenţă cu titlul „Treasure hunt: aplicație web
ce promovează cultura Iașului” este scrisă de mine şi nu a mai fost prezentată niciodată la o
altă facultate sau instituţie de învăţământ superior din ţară sau străinătate. De asemenea,
declar că toate sursele utilizate, inclusiv cele preluate de pe Internet, sunt indicate în lucrare,
cu respectarea regulilor de evitare a plagiatului:
toate fragmentele de text reproduse exact, chiar şi în traducere proprie din altă limbă,
sunt scrise între ghilimele şi deţin referinţa precisă a sursei;
reformularea în cuvinte proprii a textelor scrise de către alţi autori deţine referinţa
precisă;
codul sursă, imaginile etc. preluate din proiecte open-source sau alte surse sunt
utilizate cu respectarea drepturilor de autor şi deţin referinţe precise;
rezumarea ideilor altor autori precizează referinţa precisă la textul original.
Iaşi, data
Absolvent Prenume Nume
_________________________
(semnătura în original)
4
DECLARAŢIE DE CONSIMŢĂMÂNT
Prin prezenta declar că sunt de acord ca Lucrarea de licență cu titlul „Titlul complet al
lucrării”, codul sursă al programelor şi celelalte conţinuturi (grafice, multimedia, date de test
etc.) care însoţesc această lucrare să fie utilizate în cadrul Facultăţii de Informatică.
De asemenea, sunt de acord ca Facultatea de Informatică de la Universitatea „Alexandru Ioan
Cuza” Iași să utilizeze, modifice, reproducă şi să distribuie în scopuri necomerciale
programele-calculator, format executabil şi sursă, realizate de mine în cadrul prezentei
lucrări de licenţă.
Iaşi, data
Absolvent Prenume Nume
_________________________
(semnătura în original)
5
Contents
Introducere .................................................................................................................................................7
Contribuții ...................................................................................................................................................9
1. Tehnologii folosite............................................................................................................................. 10
1.1 Java .................................................................................................................................................. 10
1.2 Spring MVC Framework ............................................................................................................... 11
1.3 Apache Tomcat ............................................................................................................................... 12
1.4 Apache Maven ................................................................................................................................ 12
1.5 ORM-ul Hibernate ............................................................................................................................ 13
1.6 JavaServer Pages (JSP).................................................................................................................. 13
1.7 jQuery .............................................................................................................................................. 14
1.8 Materialize CSS .............................................................................................................................. 14
1.9 Apache Tiles ..................................................................................................................................... 14
2. Arhitectura aplicației ....................................................................................................................... 15
2.1 Presentation Layer ......................................................................................................................... 15
2.2 Business Layer .................................................................................................................................. 16
2.3 Data Access Layer ............................................................................................................................ 16
2.4 Baza de date ..................................................................................................................................... 17
3. Implementarea aplicației ................................................................................................................. 18
3.1 web-context.xml .............................................................................................................................. 18
3.2 application-context.xml .................................................................................................................. 18
3.3 pom.xml .......................................................................................................................................... 20
3.3 Maparea unei entități cu baza de date .......................................................................................... 20
3.4 Extragerea informațiilor din baza de date ..................................................................................... 22
3.5 Relația URL - contoller .................................................................................................................... 23
3.6 Autentificarea .................................................................................................................................. 23
3.7 Trimiterea de mail-uri ...................................................................................................................... 24
3.8 Încărcarea de imagini din browser .................................................................................................. 25
3.9 Interceptarea fiecărei cereri ............................................................................................................ 27
3.10 Procesarea și compararea de imagini ........................................................................................... 28
3.11 Afișarea timpului unei știri ............................................................................................................ 32
4. Algoritmii de procesare de imagine ................................................................................................ 34
4.1 Algoritmul ASIFT .............................................................................................................................. 34
6
4.2 Algoritmul SIFT ................................................................................................................................. 35
5. Strategii de îmbunatățire a performanțelor ................................................................................... 38
5.1 Memorarea locala a unor resurse. .................................................................................................. 38
5.2 Accesarea resurselor doar atunci când sunt cerute ....................................................................... 39
6. Utilizarea aplicației ........................................................................................................................... 41
6.1 Pagina de misiuni ............................................................................................................................. 41
6.2 Accesarea unei misiuni .................................................................................................................... 41
6.3 Pagina de ştiri ............................................................................................................................... 43
6.4 Clasamentul .................................................................................................................................. 44
6.5 Pagina de profil ............................................................................................................................ 44
6.6 Pagina administratorului ........................................................................................................... 45
6.7 Prietenii ......................................................................................................................................... 46
7. Directii viitoare ale aplicatiei ........................................................................................................... 47
7.1 Spring Security ................................................................................................................................. 47
7.2 Chat între utilizatori ......................................................................................................................... 48
7.3 Transformarea aplicației într-un serviciu REST............................................................................... 48
Bibliografie ................................................................................................................................................ 50
7
Introducere
„Treasure hunt” este un joc pentru una sau mai multe persoane, de toate vârstele, ce
implică găsirea unor locaţii/obiecte şi combină elemente de orientare, artă şi rezolvare a
problemelor pe baza unor indicii. Acest tip de joc a apărut în anul 1956, fiind creat de
comediantul Jan Murray.
Treasure hunt a trecut la un alt nivel, în anul 2000, odată cu apariţia „Geocaching”-ului,
ce presupunea că fiecare participant să dispună de un sistem GPS, cu ajutorul căruia puteau
localiza diverse locaţii. Astfel, a apărut şi aplicaţia mobilă „Geocaching” ce afişează o hartă cu
mai multe marcaje. Utilizatorii trebuiau să găsească cutiile din locaţiile sugerate şi să îşi valideze
poziţia. Aplicaţia este în continuare populară, având peşte 1 milion de descărcări în magazinul
Google Play.
Alte evenimente dedicate acestui tip de joc au avut loc in 2012, unde s-a stabilit un record
mondial ca număr de participanți (466, divizați in 93 de echipe). Tot în acest an, eBay, a
organizat un astfel de joc, cu premii în valoare de $200.000.
Subiectul acestei lucrări îl reprezintă o aplicaţie web, ce susţine cultura oraşului Iaşi.
Motivul realizării aceasteia a fost pentru a susţine campania „Iaşi, Capitală Culturală Europeană
în 2021”. Între timp, Iașul a fost eliminat din cursă pentru acest titlu, lucru ce însă nu împiedică
dezvoltarea aplicaţiei în continuare. Spre deosebire de „Geocaching”, aplicaţia presupune
realizarea unor misiuni mai diferite, mergând pe ideea de gamification.
Gamification este un concept de integrare a mecanismelor de joc, cu scopul de a capta şi
motiva oamenii să-şi atingă ţelurile. Cele mai utilizatate mecanisme de joc sunt: punctele,
insignele, nivelele, clasamentul, provocările, etc. Conceptul a devenit foarte popular cu timpul,
fiind aplicat în site-uri precum: Linkedin, Stack Overflow, Amazon, SAP Community Network,
etc.
Misiunile pot reprezenta găsirea unui punct de interes cultural, edificiu sau răspunderea
la diverse întrebări ce vizează cultura Iaşului. Găsirea edificiului este validată atât prin locaţia
utilizatorului, dar şi prin procesarea imaginii pe care acesta trebuie să o realizeze.
Aşadar, într-un mod interactiv, aplicaţia îndeamnă utlizatorul să iasă din casă, să înveţe
cultura oraşului în care locuieşte şi este recompensat pentru aceasta. Recompensele contribuie la
ideea de gamification a jocului. Odată finalizată o misiune, jucătorul primeşte puncte de
experienţă, cu ajutorul cărora poate creşte în nivel sau rang. În acest sens este realizat şi un
clasament, sporind concurenţa şi motivaţia jucătorilor.
8
Pe scurt, soluţia acestei idei, o reprezentă o aplicaţie web, realizată în Java, cu ajutorul
bibliotecii Spring MVC, menită pentru a fi utilizată atat de pe calculatorul personal, cât și de pe
telefon. Alte părţi mai interesante: procesarea imaginilor (utlizarea algoritmului ASIFT),
geolocația (suport nativ în HTML5), simularea cache-ului la nivel de server, arhitectura
aplicației, tehnologiile folosite, etc.
9
Contribuții
O primă contribuție în realizarea lucrării o reprezintă etapă de cercetare în vederea
conceperii unei idei neimplementate, ce poate avea un impact în societate. Sunt de părere că un
joc, cu o idee bună, captează utilizatorul, putând să îl influenţeze într-o anumită direcție.
În realizarea lucrării de licență cu tema „Treasure hunt: aplicație web ce promovează
cultura Iașului” am propus o abordare proprie și originală a realizării unui joc. Ideea reprezintă
automatizarea jocului de Treasure Hunt, particularizandu-l pe subiecte ce vizează istoria, cultura,
arta unei comunități. Scopul lucrării îl reprezintă asimilarea informațiilor culturale ce vizează
orașul în care locuim (Iași). Consider că astfel de informații culturale se asimiliează mult mai
repede printr-un joc competitiv ce interacționează cu realitatea. Jucătorul este practic îndemnat
să se informeze despre cultura orașului în vedearea avansării în joc.
Soluția propusă (automatizarea jocului) nu se bazează pe supervizarea unor coordonatori
pentru validarea unor stagii, cum este prevăzut în jocul clasic de „Treasure Hunt”. În acest scop
am creat o aplicație web ce implementează problema de mai sus, creată folosind tehnologii open-
source, robuste.
Aplicația respectă principiile SOLID şi se bazează pe o structură modularizată,
implementată în așa fel încât adăugarea de noi funcționalități sau orice modificări aduse să nu
afecteze bună funcționare a celorlalte componente.
O parte interesantă a aplicației o reprezintă lipsa dependeței de un supervizor uman care
să valideze misiunile (duse sau nu la bun sfârșit). Validarea unei misiuni, ce presupune
identificarea unui edificiu, prezintă un subiect complex şi de mare interes. Acest lucru implică
identificarea locației jucătorului și validarea pozei pe care acesta trebuie să o realizeze.
În privinţa procesării imaginii, am ales să utilizez algoritmul ASIFT (cu anumite condiții
impuse) ce ajută în extragerea unor trăsături din imagini. Apoi s-au testat diferiţi comparatori de
trăsături pentru a identifica unul ce se potriveşte pe modele arhitecturale, precum clădirile.
O altă contribuție interesantă este redată de adoptarea unor strategii de îmbunătăţire a
performanțelor aplicaţiei. Aceste strategii implică încercarea realizării a câtor mai puţine accesări
la baza de date, precum şi procesări de imagini pe fire de execuţie separate.
Așadar, lucrarea prezintă o idee originală de joc, ce consider că poate avea un impact bun
în rândul ieşenilor. Realizarea cu succes a acesteia se bazează atât pe elemente menţionate mai
sus, cât şi pe conceptele practice şi teoretice dobândite în facultate.
10
1. Tehnologii folosite
1.1 Java
Limbajul de programare Java este unul dintre cele mai populare limbaje orientate obiect,
având următoarele catacteristici:
Dinamicitate: reține cantităţi substanțiale de infomații la runtime
Simplitatea: sintaxă similară cu cea din C/C++, simplificată
Robustețea: nu permite suprascrierea memoriei prin renunțarea la pointeri, ceea
ce poate preveni coruperi de date. Nu permite moştenirea multiplă iar dealocarea
momeriei reprezintă o sarcină de care se ocupă o procedură ce rulează în fundal
(„Garbage Collector”)
Complet orientat obiect
Portabilitatea: se poate executa pe orice platformă; poate fi transferat și pe web
(appleturi)
Conceptul de pointer nu există în Java, însă se bazează pe referinţe pentru a păstra
adresele de memorie ale obiectelor. Alt concept care lipseşte este acela de destructor, fiind
introdus Garbage Collector-ul. Deși monștenirea multiplă este eliminată, programatorul poate
simula acest lucru prin moștenirea înlănțuită. Spre deosebire de multe limbaje, Java este şi
interpretat şi compilat, caracteristică ce îi redă portabilitatea. În procesul de compilare Java
prezintă doi pași mari. În primul, codul sursă este transformat în bytecode, urmând ca în al doilea
pas, masina virtuală Java (JVM) să interpreteze acest cod intermediar.
Sun Microsystems (achiziţionat ulterior de Oracle) a dezvoltat patru ediţii Java, ţintind
spre diferite medii de dezvoltare :
Java Card, pentru carduri inteligente
Java Micro Edition, pentru medii cu resurse limitate
Java Standard Edition, pentru staţiile de lucru comune
Java Enterprise Edition, pentru aplicaţiile enterprise
11
Figura 1: Java-portabilitate pe diferite platforme 1
1.2 Spring MVC Framework
Biblioteca Spring MVC este una dedicate Web-ului, oferind o arhitectură Model-View-
Controller şi componente ce ajută în crearea de aplicaţii web flexibile. Şablonul MVC ajută în
separarea diferitelor aspecte ale unei aplicaţii, în timp ce menţine un cuplaj slab între aceste
elemente (nu există dependinţe puternice).
Acestă bibliotecă se bazează pe un servlet, numit DispatcherServlet, care preia toate
cererile şi răspunsurile HTTP. În diagrama de mai jos, se ilustrează cum servletul procesează o
cerere.
Figura 2: Fluxul realizat de DispatcherServlet 2
1 Sursă: http://www.slideshare.net/laxmanpuri71/java-byte-code-virtual-machine 2 Sursă: http://www.tutorialspoint.com/spring/spring_web_mvc_framework.htm
12
1.3 Apache Tomcat
Apache Tomcat este un server-web și container de servleturi open-source. Acesta
implementează diverse specificații Java Enterprise Edition, precum Java Servlet, Java Servlet
Pages, WebSocket, etc. Prezintă trei componente:
Catalina – este numele containerului de servlet, a cărui funcționalitate este de a
implementa specificaţiile Sun Microsystems pentru servlet şi JSP (Java Server
Pages). Catalina este integrat în orice tip de platformă unde informațiile necesare
autentificării sunt deja create şi menținute
Coyote – Această componentă ascultă pentru conexiuni noi la server pe un port
TCP și trimite cererea către motorul Tomcat pentru a o procesa, clientul
primițând astfel un răspuns. Ca şi server web, suportă protocolul HTTP 1.1, fiind
componenta de conectare. Permie oricărui container JSP să se comporte ca un
server web.
Jasper – cunoscut și ca „Tomcat JSP Engine”, este motorul folosit de Tomcat
pentru fişierele JSP , în sensul că parsează le parsează și le compilează în cod
Java ca şi servleturi ce pot fi folosite de Catalina. Schimbările in JSP-uri sunt
detectate, și salvate automat, ajutând dezvoltatorul, întrucât serverul nu mai
trebuie restartat.
1.4 Apache Maven
Maven este un instrument pentru automatizarea build-ului, utilizat în proiecte Java.
Acesta descrie construcția unei aplicații, adresându-se totodată și asupra dependențelor
(pachetele de care are nevoie proiectul Java). Aplicația este construită pe baza unui fișier XML,
numit POM (Project Object Model), ce specifică dependențele către alte module sau componente
externe. Așadar, la build, toate bibliotecile necesare sunt preluate din cache-ul local iar în cazul
în care acestea lipsesc, sunt descăcate automat.
Spre deosebire de tehnologiile similare (Ant, Gradle), Maven introduce conceptele de
artifact, repository, ceea că ajută mult la depănare, versionare şi documentare a aplicației.
Orice proiect Maven are o structură predefinită, evidențiată mai jos:
13
Figura 3: Structura unui proiect Maven
1.5 ORM-ul Hibernate
Hibernate este o bibliotecă open-source ce simplifică dezvoltarea aplicaţiilor Java care
interacţionează cu bazele de date. Acesta oferă o soluţie de mapare a unui model orientat obiect
în bazele de date clasice, fiind independent de sistemul de gestiune a bazei de date. Reprezintă o
implementare a interfeţei JPA (Java Persistence API), însemnând că poate fi integrat în orice
sistem ce suportă JPA (aplicaţii pe platformele Java SE, EE, etc).
O caracteristică enețială o reprezintă performanţă, hibernate având diverse strategii de
fetching, precum iniţializarea leneşă (un obiect este extras din baza de date, doar atunci când este
cerut).
Hibernate are de asemenea şi propriul limbaj pentru interogări, numit HQL (Hibernate
Query Language), ce permite scrierea de cod SQL utilizând obiectele Java. În plus, biblioteca
generează majoritatea codului SQL în timpul instanțierii obiectului, nu la runtime.
1.6 JavaServer Pages (JSP)
Jsp este o tehnologie ce rulează pe partea de server şi ajută în crearea dinamică de
conţinut HTML. O particularitate a acestei tehnologii este că permite inserarea de scriplet-uri
(cod Java) prin delimitatorii <%= [...] %>
Un compilator JSP este un program care parsează JSP-urile şi le transformă în servleturi
Java executabile. Randarea codului HTML la nivel de server poate reprezenta un avantaj,
14
întrucât vă avea aceleaşi performațe indiferent de platforma clientului (dat fiind faptul că pe
mobil, operaţiile DOM sunt mult mai lente).
1.7 jQuery
jQuery este unul dintre cele mai populare biblioteci de JavaScript, conceput pentru a
uşura şi îmbunătăţi procese asupra arborelui DOM, cereri AJAX, animaţii, etc. Javascript este un
limbaj de programare orientat obiect bazat pe conceptul prototipurilor, rulat direct în browser.
Are rol în introducerea unor funcţionalităţi în paginile web.
Motivul alegerii jQuery se datorează faptului că scurtează substanţial atât timpul de
dezvoltare, precum şi lungimea codului JavaScript.
1.8 Materialize CSS
Materialize este o bibliotecă de CSS, ce ajută în crearea de site-uri şi aplicaţii web.
Conţine şabloane bazate pe HTML și CSS pentru tipografie, formulare, butoane navigare şi alte
elemente de interfaţă precum şi extensii opţionale JavaScript. Se bazează pe ideea de „Material
Design”, promovată de Google şi utilizată în majoritatea aplicaţiilor lor.
1.9 Apache Tiles
Apache Tiles este un „templating framework” pentru aplicaţiile Java moderne. Se
bazează pe şablonul de proiectare Composite şi ajută la dezvoltarea paginilor web. Şablonul
Composite sugerează că un grup de obiecte similare să fie tratate ca aceeaşi instanţă a unui
obiect. În cazul paginilor web, în layout (header şi footer) sunt introduse JSP-urile returnate de
Controller.
<tiles:insertAttribute name="body" />
Inserarea de JSP-uri se face prin:
<put-attribute name="body" value="/WEB-INF/views/main/register.jsp" />
Așadar, la schimbarea conținutului paginei web, header-ul si footer-ul nu vor mai fi reîncărcate.
15
2. Arhitectura aplicației
Aplicația se bazează pe o arhitectură „server-side”. În modulul de server, se remarcă 3 mari
straturi:
Presentation Layer
Business Layer
Data Access Layer
Fiecare strat „ştie” foarte puţin din codul celorlalte, doar cât este suficient pentru a realiza
sarcinile necesare. De menţionat faptul că Presentation Layer nu poate comunica direct cu Data
Acces Layer (şi invers). Comunicarea se realizează strict prin Business Layer.
Figura 4: Arhitectura aplicației 3
2.1 Presentation Layer
Partea de prezentare este organizată pe baza şablonului MVC (Model-View-Controller),
unde este de mare ajutor biblioteca Spring MVC. Cererea HTTP este preluată de controller, care
la rândul său decide dacă trebuie să apeleze stratul de business. Dacă acest lucru nu este necesar,
3 Sursă: http://terasolunaorg.github.io/guideline/1.0.1.RELEASE/en/Overview/SpringMVCOverview.html
16
controller-ul va returna direct un view. Însă, dacă în caz contrar, va interacţiona cu Business
Layer-ul, care la rândul său va comunica mai departe cu al treilea modul.
Toate controller-ele definite de dezvoltator, în Spring, trebuie să implementeze interfața
Controller (@Controller) sau să extindă AbstractController. Partea de model din layer nu se
referă la entităţile utilizate în DOA, ci la modelul pe care îl poate returna un controller. Acesta
este practic un „map” ce va fi ataşat unui view. În JSP-uri, acest model poate fi accesat prin
sintaxa ${cheieMap}.
2.2 Business Layer
Acest layer conţine funcţionalităţile de bază ale aplicaţiei. Practic, acest modul determină
cum informaţiile sunt create, afişate, stocate şi schimbate, primind comenzi de la Presentation
Layer şi apelând mai departe Data Access Layer.
În acest modul, regăsim clasele adnotate cu @Service, fiind un specific al framework-
ului Spring. Acest lucru indică faptul că o clasă respectă practica DDD (Domain driven design),
unde modelul rămâne încapsulat pe partea de server, fiind reprezentat doar la nivel de servicii şi
repository-uri (persistentă a datelor). Aceste servicii respectă principiul singurei responsabilități.
În cazul aplicaţiei noastre, există câte un serviciu pentru: fluxul jocului, upload-ul imginilor,
procesarea imaginilor, trimitere de mail-uri, etc. Gruparea serviciilor, în aşa fel încât să aibă
fiecare o singură responsabilitate, reduce impactul schimbărilor ulterioare, codul fiind mult mai
uşor de menţinut.
2.3 Data Access Layer
DAO: Data Acces Object este de fapt un şablon de proiectare folosit în separarea API-
urilor low-level de accesare a datelor de serviciile business high-level. În general,
şablonul este asociat cu aplicaţiile Java EE şi bazele de date relaţionare, accesate prin
JDBC, fiind specificat în ghidul de practici bune al „Sun Microsystems”. Acesta
încapsulează toate accesele la baza de date. DAO realizează managementul conexiunii cu
sursa de date pentru a obţine şi pastra informaţii. Este format din 3 componente:
interfaţa DAO (defineşte operaţiile standard ce pot fi realizate pe model),
implementarea interfeţei DAO (implementarea interfeţei de mai sus), obiectele Model
(POJO-uri, conţinând metode set şi get). Mapând apelurile aplicaţiei asupra acestui strat,
DAO oferă operații cu date specifice, însă nu expune informaţii sensibile despre baza de
date.
Avantajele folosirii acestui şablon au impact în separarea a două părţi importante ale
aplicaţiei (Baza de date - Data Access Layer) care ar trebui să se dezvolte independent,
neştiind prea multe una despre cealaltă. Aşadar, modificări asupra logicii de acces a
obiectelor se pot realiza în DAO, însă schimbări în partea de persistenţă nu ar trebui să
influenţeze clasele DAO, în cazul în care interfaţa este corect implementată.
17
Figura 5 Schema DAO 4
Entitățile: reprezintă obiectele model, menţionate mai sus. Aceasta sunt evidenţiate prin
adnotarea @Entity, fiind mapate cu tabelele din baza de date prin @Table. O coloană
este mapata prin @Column, fiind de fapt un obiect Java, remarcându-se că numele
acesteia nu trebuie să coincidă cu cel al coloanei din tabel.
2.4 Baza de date
Figure 6 Schema bazei de date
4 Sursă: http://www.oracle.com/ocom/groups/public/@otn/documents/digitalasset/146804.jpg
18
3. Implementarea aplicației
3.1 web-context.xml
Acest fişier conţine detaliile de configurare care ajută serverul Tomcat, oferind mai multe
informaţii (menţionate mai jos).
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd">
<mvc:annotation-driven />
<mvc:resources mapping="/resources/**" location="/resources/" />
<mvc:interceptors>
<bean class="interceptor.RequestInterceptor" />
</mvc:interceptors>
<bean id="viewResolver"
class="org.springframework.web.servlet.view.UrlBasedViewResolver">
<property name="viewClass"
value="org.springframework.web.servlet.view.tiles3.TilesView" />
</bean>
<bean class="org.springframework.web.servlet.view.tiles3.TilesConfigurer"
id="tilesConfigurer">
<property name="definitions">
<list>
<value>/WEB-INF/views/**/tiles-definitions.xml</value>
</list>
</property>
</bean>
</beans>
Sunt specificate unde sunt plasate resursele aplicaţiei, putand fi accesate doar din această
locaţie. Este menţionat un interceptor care filtrează toate cererile din aplicaţie, fiind utilizat
pentru controlul accesului şi nu numai. De asemenea, web-context.xml specifică (printr-un
pattern) şi locul în care se găsesc definiţiile pentru Apache Tiles.
3.2 application-context.xml
Reprezintă interfaţa centrală dintr-o aplicaţie Spring, ce conţine informaţii de configurare
a acesteia. La run-time este read-only, dar poate fi reîncărcat dacă este necesar.
ApplicationContext poate încărca java beans, la cerere le poate distribui, şi le poate lega
împreună. Prezintă interes deoarece adaugă funcţionalități specifice aplicaţiilor enterprise.
19
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jee="http://www.springframework.org/schema/jee"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/jee
http://www.springframework.org/schema/jee/spring-jee-3.1.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd">
<context:component-scan base-package="demo" />
<context:annotation-config />
<bean id='entityManagerFactory'
class='org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean'>
<property name="persistenceUnitName" value="Skelet" />
<property name='dataSource' ref='dataSource' />
<property name="jpaVendorAdapter">
<bean id="jpaAdapter"
class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="databasePlatform"
value="org.hibernate.dialect.PostgreSQLDialect" />
<property name="generateDdl" value="true" />
<property name="showSql" value="true" />
<property name="format_sql" value="true"></property>
</bean>
</property>
</bean>
<bean id="txManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<tx:annotation-driven transaction-manager="txManager" />
</beans>
În acest fișier este specificat:
Locul unde se găsesc toate clasesle ce conţin adnotările @Controller, @Repository,
@Service, etc (base-package="demo"). În cazul în care aceste clase sunt găsite, Spring le
va înregistra în „bean-factory”
beanul entityManagerFactory, este cel care se ocupă de proprietăţile JPA (Java
Persistence API), făcând referire la proprietatea 'dataSource'. Aceasta conţine
informaţii ce ajută la stabilirea conexiunii cu baza de date.
Beanul txManager: este menţionată clasa JpaTransactionManager care este responsabilă
de realizarea tranzacţiilor de informaţii între baza de date şi aplicaţie. Având aceste
configurări, în implementările DAO putem injecta entityManagerul:
@PersistenceContext
private EntityManager em;
20
3.3 pom.xml
Project Oriented Mode reprezintă fişierul de configuraţie a bibliotecii Maven. Conține
informaţiile necesare bibliotecii să construiască un proiect. Printre elemente se numără
dependenţele proiectului, versiunea proiectului, numele dezvoltatorilor, descriera, etc.
Un astfel de fişier trebuie să conţină groupId (id-ul grupului proiectului, de regulă fiind
unic pe companie), artifactId (id-ul proiectului), version (versiunea proiectului).
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>demo</groupId>
<artifactId>demo</artifactId>
<version>1.0</version>
<packaging>war</packaging>
<name>Skelet</name>
<description>Skelet POM</description>
<repositories>
<repository>
<id>OpenIMAJ maven releases repository</id>
<url>http://maven.openimaj.org</url>
</repository>
<repository>
</repositories>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.3.0</version>
</dependency>
În pom-ul proiectului avem specificat goupId-ul şi artifactId-ul ca fiind „demo”, cu
versiunea 1.0. De asemenea este menţionat modul de împachetare al proiectului, în acest caz
fiind war (web application archive). Elementul „repository” marchează (în acest caz) un
repository remote. Acest lucru este folositor în momentul în care Maven nu găseşte dependenţele
în repository-ul central. Cum biblioteca OpenIMAJ nu se regăsește acolo, Maven va căuta în url
precizat mai sus.
Pentru exemplu, este specificată şi o dependenţă, în cazul nostru, biblioteca Jackson
(responsabilă de conversia automată din obiecte Java în obiecte JSON). La momentul build-ului,
dacă această bibliotecă nu se găseşte în cache-ul local, Maven o va descărca şi o va injecta la
dependenţele proiectului.
3.3 Maparea unei entități cu baza de date
Acest lucru se realizează cu ajutorul bibliotecii de persistenţă Hibernate. O clasă
(entitate) va corespunde unui tabel din baza de date.
@Entity
@Table(name = "Quest")
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
21
@DiscriminatorColumn(name = "type", discriminatorType =
DiscriminatorType.STRING)
@DiscriminatorValue(value = "quest")
public class Quest {
@Id
private String id;
@Column(name = "difficulty")
private String difficulty;
@Column(name = "description")
private String description;
@OneToOne
@JoinColumn(name = "hint_id")
@Cascade(CascadeType.ALL)
private Hint hint;
@Column(name = "info")
private String info;
@Column(name = "next_quest")
private String nextQuest;
@Column(name = "available")
private boolean available;
@Column(name = "date")
private Date date = new Date();
Prin adnotarea @Entity marcăm clasa „Quest” ca fiind un bean, fiind mapată de tabelul
„Quest” prin @Table (name = "Quest"). O regulă a Hibernate-ului este ca orice entitate să aibă o
cheie primară, specificată prin @Id. @Column se foloseşte pentru a face maparea dintre
proprietatea clasei şi coloanele tabelului. De menţionat faptul că numele tabelului, coloanei nu
trebuie să corespundă cu numele clasei, respectiv proprietăţii, aceste putând să difere prin
(name = „nume tabel/coloană”).
Entitatea de mai sus reprezintă un caz mai special, deoarece toate clasele care o vor
extinde, nu vor avea un tabel separat. Valorile acestora vor fi păstrate tot în acest tabel, fiind
adăugate doar noi coloane (@Inheritance (strategy = InheritanceType. SINGLE_TABLE)).
@DiscriminatorColumn va introduce o nouă coloană în tabel (nu şi o nouă variabila java), ce va
avea valoarea sugerată de @DiscrimatorValue. Aşadar, subclasele nu vor mai avea adnotarea
@Table, însă obligatoriu este să conţină valoarea discriminatorului.
@Entity @DiscriminatorValue ("localization_quest") Public class LocalizationQuest extends Quest {[..]}
Evident, vor exista coloane cu valori nule, corespunzătoare unei anumite subclase. Însă,
un avantaj semnificativ îl reprezintă faptul că la extragerea unei subclase, join-ul nu mai este
necesar pentru a obţine informaţii din superclasă.
22
3.4 Extragerea informațiilor din baza de date @PersistenceContext private EntityManager em;
@Override public List<User> getAdmins() { Query query = em.createQuery("from User user where user.access='admin'"); try { return(query.getResultList()); } catch (NoResultException e) { e.printStackTrace(); } return null; }
Acesta este un fragment de cod din implentarea unui DAO, reprezentând extragerea
utilizatorilor care au accesul de admin. EntityManager este o interfaţă cu metode pentru
interacţionarea cu contextul de persistenţă. Funcţia createQuery primeşte ca parametru un String
în formal HQL (Hibernate Query Language). Diferenţa dintre SQL şi HQL este ca ultimul
menţionat face referire la obiecte Java şi nu la elemente specifice bazei de date.
Un DAO este injectat într-un serviciu/controller prin adnotarea @Autowired. Practic, în
acest fel, rolul instantierii clasei DAO este preluat de Spring şi nu de dezvoltator. O sintaxă
precum cea de mai jos este suficientă pentru a avea un bean Spring intializat. @Autowired UserDAO userDAO;
Adnotarea se bazează pe conceptul de „Dependency Injection”, fiind un şablon de
proiectare ce implementează „Inversion of Control”. O dependenţă este un obiect de cerut (în
cazul nsotru, un serviciu sau DAO). O injecţie presupune pasarea dependenţei la un obiect
dependent care ar avea nevoie de ea. Practic, în loc de a avea obiecte care creează dependenţe,
acestea sunt pasate în constructor sau printr-un set.
„’Dependency Injection’ is a 25-dollar term for a 5-cent concept. [...] Dependency injection
means giving an object its instance variables” – James Shore5.
5 Sursă: http://www.jamesshore.com/Blog/Dependency-Injection-Demystified.html
23
3.5 Relația URL - contoller
Maparea dintre controller și url se realizează astfel:
@Controller
@RequestMapping(value = "/account")
public class AccountController {
@RequestMapping(value="/userProfile", method = RequestMethod.GET)
public String displayUserProfile( @RequestParam String id){
[...]}
}
Această metodă mapează adrese de genul: „/account/userProfile? Id= [idUser]”. În cazul
în care nu există o mapare pentru URL-ul tastat în browser, utilizatorul va fi direcţionat pe o
pagină 404, fiindui specificat că pagina căutată nu există. Prinderea erorii HTTP 404 se realizeză
prin codul de mai jos (din web.xml), fiind făcută redirectarea către o pagină proprie.
<error-page>
<error-code>404</error-code>
<location>/WEB-INF/views/error/404.jsp</location>
</error-page>
3.6 Autentificarea
Utlizatorul are posibilitatea să se autentifice printr-un formular clasic, dar mai interesantă
este cea prin reţelele sociale (Facebook, Twitter, Google+). Acest lucru se realizează cu ajutorul
servleturilor. Un servlet este o clasă Java care extinde capabilităţile unui server. Acesta preia de
regulă cereri http, putând şi să răspundă la ele. Este practic, o componentă web, deploy-ată pe
server cu scopul de a crea pagini web dinamice.
În primul rând avem un buton ce apelează servletul:
<form action="facebookLogin" method="post"> <button name="subject" type="submit" class="myButton facebookButton"> <img src="http://techulus.com/buttons/fb.png" /> </button> </form>
Acțiunea „facebookLogin” este asociată unui servlet in web.xml, astfel:
<servlet> <servlet-name>FBLoginServlet</servlet-name> <servlet-class>facebook.login.domain.LoginServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>FBLoginServlet</servlet-name> <url-pattern>/facebookLogin</url-pattern> </servlet-mapping>
Mai sus este declarat servletul „FbLoginServlet”, mapat cu clasa
facebook.login.domain.LoginServlet, având ca acţiune „/facebookLogin”. Odată apăsat butonul
de autentificare, acţiunea se mută în metoda doGet a servletului. În acest moment se apelează un
serviciu oferit de biblioteca OAuth ce securizează tranzacţia de informaţii dintre serverul
24
aplicaţiei şi serverele Facebook. Dacă utlizatorul acceptă să ofere informaţiile personale,
aplicaţia va dispune de numele, email-ul, id-ul şi poza de profil ale acestuia. După efectuarea
tranzacţiei, dacă id-ul utilizatorului nu se regăseşte în baza de date, acesta este salvat, trimiţându-
se şi un mail de bun venit.
3.7 Trimiterea de mail-uri
Spring contribuie cu un API de nivel înalt pentru simplificarea procesului de trimis
mailuri. Este Bazat pe JavaMail(un API independent de platformă și protocol ce ajută în
construirea de mail-uri).
Figura 7 Diagrama JavaMail 6
Pentru a trimite mesaje, este folosită clasa JavaMailSenderImpl. Aceasta necesită
urmatoarea declarație in application-context.xml:
<bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl"> <property name="host" value="smtp.gmail.com" /> <property name="port" value="587" /> <property name="protocol" value="smtp" /> <property name="username" value="[email protected]" /> <property name="password" value=" iasi.treasureHunt " /> <property name="javaMailProperties"> <props> <prop key="mail.smtp.auth">true</prop> <prop key="mail.smtp.starttls.enable">true</prop>
6 Sursă: http://www.codejava.net/frameworks/spring/sending-e-mail-with-spring-mvc
25
<prop key="mail.smtp.quitwait">false</prop> </props> </property> </bean>
În acest sens, s-a creat un serviciu care se ocupă de email-uri (mesaj de bun venit, mesaj
de resetare a parolei).
@Service public class MailService {
@Autowired JavaMailSender mailSender;
public void sendHelloEmail(String to, String firstname) { SimpleMailMessage message = new SimpleMailMessage(); message.setFrom("[email protected]"); message.setTo(to); message.setSubject("Bine ai venit"); message.setText("Salutare "+firstname+"\n\n Ne bucuram ca te-ai inregistrat!" ); mailSender.send(message); }
În această clasă putem injecta JavaMailSender-ul prin adnotarea @Autowired, trimiterea
mesajului realizându-se foarte uşor prin mailSender. Send (message). La rândul său, serviciul
MailService, va fi injectat în controller. Astfel, se realizează separarea dintre Business Layer şi
Presentation Layer.
3.8 Încărcarea de imagini din browser
Încărcarea se poate realiza atât din varianta desktop (file picker), cât şi din varianta
mobilă (cu ajutorul camerei). Acest lucru este implementat în următorul cod HTML:
<input type="file" name="image" id="file_uploader" accept="image/*; capture=camera"/>
Atributul „accept” specifică tipul de fişiere care pot fi acceptate la upload. Pentru a se
realiza transferul care server, este necesară o secvenţă de cod JavaScript:
function sendFileToServer(e){
file = e.target.files[0]; […] url=”/demo/upload/photo/user”; if (checkFile(file)){ data = new FormData(); data.append("image",file);
jQuery.ajax({ url: url, data: data, cache: false, contentType: false, processData: false, type: 'POST',
complete: function(data){}
})
}
}
26
Odată selectată imaginea, funcţia de mai sus este apelată. Este extrasă imaginea din
e.target.files[0] urmând să fie validată. Funcţia checkFile() verifică dacă dimensiunea imaginii
nu depăşeşte 5Mb şi dacă aceasta are într-adevăr o extensie corespunzătoare unei imaginii. În
cazul în care condiţiile sunt îndeplinite, se realizează o cerere Ajax care url-ul specificat.
AJAX (Asynchronous JavaScript and XML) reprezintă o nouă modalitate de a folosi
împreună tehnologii precum (X) HTML, CSS, JavaScript, DOM, XML, XSLT,
XMLHttpRequest, prin intermediul căreia aplicaţiile web devin capabile să realizeze actualizări
mai rapide de interfaţă, fără încărcarea întreagă a paginii.
Cererea ajax din codul de mai sus va apela o metodă din controller-ul responsabil de
încărcarea de imagini. Salvarea imaginii se realizează prin : imageService.saveImage(), a cărei
implementare este urmatoarea:
public void saveImage(String fileName,MultipartFile image, String type){ String photoPath; switch (type) { case "forUser": photoPath = DIRECTORY_PATH + fileName + "." + getImageExtension(image); break; case "forQuest": photoPath = QUEST_DIRECTORY_PATH + fileName + "." + getImageExtension(image); break; default: photoPath = HINT_DIRECTORY_PATH + fileName + "." + getImageExtension(image); File file = new File(photoPath); try { image.transferTo(file); } catch (IOException e) { e.printStackTrace(); } }
După cum se poate observa, imaginea nu este păstrată în baza de date că tip BLOB, ci
este păstrată pe o partiţie a maşinii server. După ce imaginea a fost salvată, obiectului căruia îi
este asignată imaginea (un utilizator, o misiunea sau un hint), îi este setată proprietate
„photoUrl” după modelul: „/demo/upload/getPhoto/tip/id”. Astfel putem face referire la imagini
după cum este arătat mai jos:
<img src="${user.photoUrl}" alt="profile image"/>
În momentul construirii dinamice a codului HTML, pentru a afișa imaginea de mai sus,
se va face apel la urmatoarea metoda:
@RequestMapping(value = "/getPhoto/{type}/{fileName}/{extension}", method = RequestMethod.GET) @ResponseBody public byte[] sendProfilePhoto(@PathVariable String type, @PathVariable String fileName, @PathVariable String extension) { ImageService imageService = new ImageService(); String path; switch (type) { case "user": path = ImageService.DIRECTORY_PATH + fileName + "." + extension; break; case "quest": path = ImageService.QUEST_DIRECTORY_PATH + fileName + "." + extension; break;
27
default: //hint path = ImageService.HINT_DIRECTORY_PATH + fileName + "." + extension; break; } return imageService.getImageFromPath(path); }
Calculând calea adevarată a imaginii, se va face apelul la funcția getImageFromPath() ce
va citi imaginea într-un flux de bytes și o va returna.
Concluzii
S-a ales salvarea imaginilor ca path-uri în baza de date din următoarele motive:
stocarea în baza de date este de obicei mai costisitoare
serverul nu are nevoie de procedee speciale pentru a accesa imaginile din sistem (funcţia
getImageFromPath)
bazele de date au spaţiu de stocare limitat.
Desigur, şi a doua variantă de stocare ar fi avut avantajele ei, precum păstrarea integrităţii
datelor, ceea ce sistemul de fişiere nu garantează. Însă cumulând avantajele şi dezavantejele,
stocarea în sitemul de fişiere a fost soluţia aleasă.
3.9 Interceptarea fiecărei cereri
Acest lucru se realizează extinzând clasa HandlerInterceptorAdapter din pachetul
org.springframework.web.servlet.handler. O parte restrănsă din metodă este prezentă mai jos.
@Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println(request.getRequestURI()); if(request.getRequestURI().contains("/demo/account/") || request.getRequestURI().contains("/demo/game") || request.getRequestURI().contains("/demo/log")) if(!request.getRequestURI().contains("findEmail")){ User u = (User) request.getSession().getAttribute("user"); if(u == null ){ response.sendRedirect("/demo/"); return false; } }
return true;
}
Extinzând metoda preHandle() putem realiza o anumită acţiune inante ca browser-ul să
primească răspunsul. În cazul nostru, cu această tehnică realizăm controlul accesului. Cum
utilizatorul logat se află în sesiune, dacă atributul „user” este null atunci cererea spre o anumită
resursă/pagină va sfârşi prin redirectarea spre pagina de logare. În caz contrar, utilizatorul va
acces la resursa dorită.
28
De menţionat faptul că pentru a fi recunoscut acest interceptor, trebuie declarat în web-
context.xml
<mvc:interceptors> <bean class="interceptor.RequestInterceptor" /> </mvc:interceptors>
3.10 Procesarea și compararea de imagini
Procesarea unei imagini se realizează prin intermediul algoritmului ASIFT, din cadrul
bibliotecii OpenIMAJ, a cărei funcţionalitate este explicată în unul din capitolele de mai jos.
Întreaga parte de procesare este în sarcina serviciului ImageProcessorService. O primă
funcţionalitate este cea de transforma imaginea din format MultiPartFile în format Fimage.
public static FImage multipartToFImage(MultipartFile multipartFile){
try {
return ImageUtilities.readF(ImageProcessor
.multipartToFile(multipartFile));
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
Foarte important, înainte de a extrage trăsăturile unei imagini, aceasta trebuie redimensionată,
pentru a nu pune jucatorul să aștepte foarte mult procesarea propriei imagini. S-a remarcat faptul că
algoritmul dă rezultate bune și la rezoluții de 400x600. Astfel, în implementare se află minimul dintre
înalțimea si lățimea imaginii, după care se setează pe 400 pixeli, păstrând proporțiile imaginii.
După care urmează extragerea trăsăturilor. Implementarea standard a algoritmului ASIFT
în OpenIMAJ se regăseşte în clasa ASIFTEngine, fiind instanțiată mai jos în obiectul engine.
public static LocalFeatureList<Keypoint> extractFeatures(FImage image){
LocalFeatureList<Keypoint> inputFeats = engine.findKeypoints(image);
System.out.println("Extracted : " + inputFeats.size());
return inputFeats;
}
Trăsăturile sunt păstrate într-o structură de date (LocalFeatureList<Keypoint>), fiind de
fapt o listă de puncte cheie extrase din imagine. Acestea sunt procesate în momentul în care
administratorul adaugă o nouă misiune şi sunt stocate la nivel de server, fiind serializate la
închiderea acestuia. În momentul în care administratorul introduce o misiune de localizare, un
nou fir de execuție porneşte, având ca sarcină extragerea trăsăturilor imaginii. Când utilizatorul
realizează o imagine pentru a rezolva misiunea, trăsăturile sunt extrase, apoi comparate cu cele
de pe server cu scopul de a valida misiunea.
Ultima funcţionalitate a serviciului o reprezintă compararea a două imagini. Aflarea mai
precisă a potrivirilor presupune filtrarea acestora, bazată pe un model geometric dat. Un mod de
a reuşi acest lucru reprezintă utilizarea clasei ConsistentLocalFeatureMatcher, care primeşte un
„matcher” intern (FastBasicKeypointMatcher în cazul nostru) şi un estimator
(RobustAffineTransformEstimator). Estimatorul se potriveşte pe un anumit model geometric
(transformările afine7). Numărul de potriviri este dat de metoda findMatches ().
7 Model ce păstrează colinearitatea și ponderea distanțelor la diferite transformări http://mathworld.wolfram.com/AffineTransformation.html
29
public static int getMatches(LocalFeatureList<Keypoint> firstFeats,
LocalFeatureList<Keypoint> secondFeats){
LocalFeatureMatcher<Keypoint> matcher;
RobustAffineTransformEstimator modelFitter = new
RobustAffineTransformEstimator(5.0, 1500,
new RANSAC.PercentageInliersStoppingCondition(0.5));
matcher = new ConsistentLocalFeatureMatcher2d<Keypoint>(
new FastBasicKeypointMatcher<Keypoint>(8), modelFitter);
matcher.setModelFeatures(firstFeats);
matcher.findMatches(secondFeats);
final List<Pair<Keypoint>> matches = matcher.getMatches();
System.out.println("Matches found: " + matches.size());
return matches.size();
}
După mai multe teste se remarcă faptul că RobustHomographyEstimator întoarce
rezultate mai bune decât RobustAffineTransformEstimator. Primul menţionat face referire la
„planar homographies”8, ce sunt mai generale ca transformările afine, mapând patrulatere la
patrulatere. Totuşi, codul suferă schimbări minore:
RobustHomographyEstimator modelFitter2 = new RobustHomographyEstimator(5.0,
1500, new RANSAC.PercentageInliersStoppingCondition(0.5),
HomographyRefinement.SYMMETRIC_TRANSFER);
Unde SYMMETRIC_TRANSFER semnifică faptul că punctele din prima imagine sunt
proiectate de o „homography matrix” pentru a produce noi estimări ale celei de a două imagini şi
viceversa.
Teste:
Palatul Culturii
Figura 8 RobustAffineTransformEstimator, 7 potriviri
8 http://www.cse.psu.edu/~rtc12/CSE486/lecture16.pdf
30
Figura 9 RobustHomographyEstimator, 52 potriviri
Casa Dosoftei
Figura 10 RobustAffineTransformEstimator, 12 potriviri
Figura 11 RobustHomographyEstimator, 75 potriviri
31
Imagini cu obiective diferite
Figura 12 RobustAffineTransformEstimator, 9 potriviri
Figura 13 RobustHomographyEstimator, 0 potriviri
Concluzii
procesarea unei imagini prin algoritmul ASIFT este una costisitoare, atât ca timp,
dar şi ca memorie, motiv pentru care toate imaginile sunt redimensionate. Pentru
o imagine de 400x600 pixeli o astfel de procesare durează în jur de 10 secunde.
potrivirile extrase nu sunt întotdeauna perfecte. Pot exista cazuri în care nu se
găsesc potriviri, deşi imaginile conţin acelaşi obiectiv. Un astfel de caz este
reprezentat de efectul puternic de relief, spre exemplu:
32
Figura 14 0 potriviri, indiferent de estimator
estimatorul afin, produce în medie mai puţine potriviri, acestea putând fi şi
eronate în cazul imaginilor ce conţin obiecte total diferite, cum este evidenţiat în
ultimul test.
3.11 Afișarea timpului unei știri
În pagina de newsfeed, este vizibil timpul trecut din momentul apariţiei unei ştiri. Acest
lucru se realizează cu ajutorul bibliotecii open-source numită Timeago9, al cărei autor este Ryan
McGeary. Interesant în implementare este faptul că biblioteca poate primi ca parametru chiar
java.util.Date, astfel:
<time class="timeago" datetime="${newsfeed.endDate}"></time> .
La momentul încărcării codului HTML, este apelată biblioteca cu următorul cod
JavaScript:
jQuery(document).ready(function() {
jQuery("time.timeago").timeago();
});
9 Biblioteca Timeago: http://timeago.yarp.com/
33
Extinderea bibliotecii Timeago pentru a returna timpul calculat, cu termeni în limba
romană, este urmatoarea:
strings: {
prefixAgo: "Acum",
prefixFromNow: null,
suffixAgo: null,
suffixFromNow: null,
inPast: 'Orice moment',
seconds: "",
minute: "1 min",
minutes: "%d min",
hour: "1 ora",
hours: "%d ore",
day: "1 zi",
days: "%d zile",
month: "1 luna",
months: "%d luni",
year: "1 an",
years: "%d ani",
wordSeparator: " ",
numbers: []
}
34
4. Algoritmii de procesare de imagine
4.1 Algoritmul ASIFT
Acronimul provine de la Affine Scale-Invariant Feature Transform, ASIFT fiind practic o
îmbunătăţire a algoritmului SIFT, care va fi detaliat mai jos.
Problemă: Conţin imaginile acelaşi obiect?
Soluţie: DA, deşi pozele sunt realizate din unghiuri diferite
Figura 15 ASIFT - Potrivirile găsite
Utlizand acest algoritm, este posibilă recunoaşterea obiectelor solide într-o imagine digitală,
indiferent de unghi, distanţă, atât timp cât rezoluția permite acest lucru. Dacă un obiect fizic are
margini netede, imaginea realizată de orice cameră digitală din diferite poziţii va prezenta aceste
margini tot netede, dar deformate. Aceste deformaţii sunt aproximate de transformările afine.
Algoritmul simulează imaginea iniţială, obţinută prin modificarea orientării camerei, simulare ce
nu este tratată de algoritmul SIFT, apoi aplică SIFT.
Un algoritm de potrivire a imaginilor afine invariante trebuie să acopere cei 6 parametri afini.
Metoda SIFT tratează doar 4 dintre aceştia, normalizând rotaţiile, translaţiile şi simulând zoom-
urile.
35
Figura 16 ASIFT - privire de ansamblu 10
După cum este ilustrat în imagine, ASIFT completează SIFT, simulând doi parametri care
modelează axa de direcţie a unei camere foto: scala și unghiurile de latitudine şi longitudine ale
camerei, normalizând translaţia şi rotaţia.
Paşii algoritmului:
imaginea este transformată prin simularea tuturor distorsionărilor afine cauzate de
modificiarea axei de orientare a camerei. Distorsionările depind de longitudine (φ) şi
latitudine (θ).
distorsionările sunt apoi rotite la unghiul φ, urmate de înclinări cu parametrul
t = 1/| cos θ|. Aceste rotaţii şi înclinări sunt realizate pentru un număr mic, finit de
unghiuri de latitudine şi longitudine.
toate imaginile simulate sunt comparate de SIFT
4.2 Algoritmul SIFT
Acest algoritm prezintă 2 mari stagii:
Detecţia punctelor de interes
Generarea descriptorilor
Pentru a explica ce reprezintă un punct de interes vom folosi un exemplu: Este dată poza unei
uşi. Aceasta are 4 colţuri, fiind de asemenea şi punctele de interes ale imaginii. Apoi imaginea
este rotită, fiind alterată şi dimensiunea acesteia. Imaginea iniţială poate fi comparată cu cea
modificată prin aceste 4 puncte, deoarece la scalare/rotaţie punctele nu se schimbă; uşa tot va
avea 4 colţuri. Aşadar, pe acestea le numim puncte de interes.
10 Sursă: http://www.ipol.im/pub/art/2011/my-asift/
36
Figura 17 SIFT - puncte de interes 11
În pasul următor, se filtrează punctele de interes, fiind eliminate muchiile sau
zonele cu contrast scăzut. Apoi se asignează fiecărui punct o orientare. Acest lucru,
practic, anulează orice efect de orientare al imaginii, făcând-o invariantă la rotaţii.
Figura 18 SIFT - puncte de interes cu orientare 12
11 Sursă: http://www.cc.gatech.edu/~hays/compvision/results/proj2/jting8/index.html 12 Sursă: http://www.codeproject.com/Articles/619039/Bag-of-Features-Descriptor-on-SIFT-Features-with-O
37
Pentru generarea punctelor de interes sift, numite şi descriptori, algoritmul calculează
gradientul imaginii. Apoi va forma histograme ale orientărilor. Aceste histograme măsoară
puterea gradientului în fiecare direcţie. Formând mai multe histograme şi concatenandu-le se
obţine o histogramă (Histograma gradienților orientați13) finală ce va reprezenta descriptorii
(utilizaţi în găsirea similarităţilor dintre două imagini).
Figura 19 Puncte de interes, gradienți, descriptori 14
13 https://en.wikipedia.org/wiki/Histogram_of_oriented_gradients 14 Sursă: http://www.codeproject.com/Articles/619039/Bag-of-Features-Descriptor-on-SIFT-Features-with-O
38
5. Strategii de îmbunatățire a performanțelor
Adoptând unele tehnici de îmbunătățire a performanţelor, s-a ajuns ca unele pagini să se
încarce şi de două ori mai rapid.
5.1 Memorarea locala a unor resurse.
Încărcarea paginilor depinde foarte mult de timpul de execuţie a metodei din controller,
corespunzătoare unei rute. S-a remarcat faptul că accesul multiplu la baza de date generează
timpi suplimentari de execuţie. Aşadar, o soluţie găsită este de a simula cache-ul la nivel de
server.
În serviciul CachedData avem stocaţi toţi utilizatorii şi misiunile deoarece aceste resurse au
fost cele mai acesate din baza de date.
@Service
public class CachedDataImpl implements CachedData {
private Map<String, User> users ;
private Map<String, Quest> quests ;
Pe lângă setteri şi getteri, această clasă conţine metode utilitare, ce ajută în realizarea
operaţiilor CRUD15 pe resursele stocate.
CachedData poate fi injectată în controller, accesul la resurse realizându-se mult mai rapid
după cum arată şi următorul caz de test:
Accesul la resurse doar prin baza de date:
@RequestMapping("/test1")
@ResponseBody
public double test1(){
double start, finish;
start = System.currentTimeMillis();
List<User> users = cachedData.getUsers();
List<Quest> quests = cachedData.getQuests();
questDAO.getQuest("213edba8-ac55-40f5-b9c7-c1d292096fab");
questDAO.getQuest("06f8e1c4-af2b-4d03-ab12-b43565b21c4a");
userDAO.findUser("1");
userDAO.findUser("2");
finish = System.currentTimeMillis();
return finish - start;
}
Rezultat (in functie de strategia de performanta pe care o adopta
calculatorul pe care serverul ruleaza)
o 360ms in medie (putere scazuta)
o 140ms in medie (putere mare)
Accesul la resurse prin datele stocate pe server.
15 Create, Read, Update, Delete
39
@RequestMapping("/test2")
@ResponseBody
public double test2(){
double start, finish;
start = System.currentTimeMillis();
List<User> users = cachedData.getUsers();
List<Quest> quests = cachedData.getQuests();
cachedData.getQuest("213edba8-ac55-40f5-b9c7-c1d292096fab");
cachedData.getQuest("06f8e1c4-af2b-4d03-ab12-b43565b21c4a");
cachedData.getUser("1");
cachedData.getUser("2");
finish = System.currentTimeMillis();
return finish - start;
} Rezultat:
o 0ms (putere scazuta)
o 0ms (putere mare)
Desigur, pagina nu se va incărca instant nici prin a doua metoda, acest lucru fiind dependent
și resursele CSS, JS și viteza de internet a utilizatorului.
5.2 Accesarea resurselor doar atunci când sunt cerute
După cum s-a mai menționat, JSP-uri pot primi din controller un model la care are acces prin
${numeModel}.
Se evită accesarea resurselor şi popularea modelului cu informaţii pe care clientul nu le
observă la încărcarea paginii, accesul acestora fiind făcut ulterior, dacă este cerut.
Spre exemplu, în pagina de profil, utilizatorul poate vedea relaţia cu ceilalţi jucători
Figura 20 Relatia dintre jucatori
40
Dacă acesta nu este interesat de cine îl urmăreşte, nu se va mai realiza o interogare inutilă
pe bază de date. În schimb, dacă doreşte să vadă, în momentul apăsării pe fila „Urmărit de”, se
efectuează următorul apel Ajax:
function showFollowers() {
if (notLoaded) {
notLoaded = false;
$.ajax({
url : "/demo/account/getFollowers?userId=" + userId
})
.then(
function(data) {
for (index in data) {
user = data[index];
$('#followers').append(
'<div class="row"><a id="navbarImg"
href="/demo/account/userProfile?id='
+ user.id + '" ><img class="circle responsive-img small" '+
'src=" '+ user.photoUrl +'" /> '
+ user.firstName + " " + user.lastName + ' </div>')
}
})
}
}
„notLoaded” este o variabilă care indică dacă interogarea de mai sus a mai fost realizată sau
nu. În caz afirmativ, cererea Ajax nu mai este făcută. În secvenţa „for”, este populat elementul
HTML cu id-ul „followers”.
Executând metoda de mai sus, se ajunge la urmatorul rezultat:
Figura 21 Exemplu de resurse cerute prin AJAX
41
6. Utilizarea aplicației
6.1 Pagina de misiuni
Odată logat, utilizatorul (indiferent de gradul de acces) poate vedea pagina de misiuni.
Aceste misiuni sunt sortate pe 3 categori:
misiuni noi: cele proaspăt adăugate de administratori, pe care utilizatorul încă nu
le-a accesat.
misiuni începute: cele care au fost accesate în trecut.
misiuni terminate: cele pentru care s-a oferit deja un răspuns, fie el corect sau nu.
Figure 22 Pagina de misiuni
6.2 Accesarea unei misiuni
Misiunile sunt împărţite pe doua categorii: cele de localizare a unui edificiu şi întrebările
de cultură generală. Mai jos este pagina de accesare a unei misiuni de localizare. Aceasta
prezintă informaţii specifice, precum dificultatea, descrierea şi indicaţii de realizare a pozei. De
asemenea, în partea dreaptă este afişat numărul de hint-uri disponibile. Un hint poate fi folosit
dacă misiunea are unul asignat.
42
Figura 23 Misiunea de localizare
În momentul realizării pozei, locaţia utilizatorului este preluată şi comparată cu cea a
edificiului. Dacă distanţa este mai mică de 50 metri (se ia în calcul şi eroarea localizării), atunci
se trece la pasul ce vizează procesarea imaginii. Pentru ca procesarea să nu dureze exagerat de
mult imaginea este redimensionată (minimului dintre înălţime şi lăţime i se atribuie valoarea de
400 pixeli, păstrând proporţiile dintre cele două dimensiuni). Având trăsăturile imaginii din baza
de date încărcate în memoria serverului, acestea sunt comparate cu cele extrase din poza realizată
de jucător. Dacă numărul de potriviri depăşeşte 20, înseamnă ca jucătorul a finalizat cu succes
misiunea. Ambele sunt acoperite mai jos.
Figura 24 Rezultatul terminării unei misiuni
De asemenea următoarea imagine prezintă o întrebare de cultură generală, cu informaţii
aferente acesteia.
43
Figura 25 Intrebare de cultură generală
Realizarea unei misiuni este recompensată cu puncte de experienţă în funcţie de
dificultatea acesteia. Acumularea de puncte experienţă conduce la creşterea în nivel şi în rank.
Jucătorul beneficiază de un hint în plus la fiecare creştere de nivel.
6.3 Pagina de ştiri
În această pagină utlizatorul poate regăsi informaţii ce au legătură cu jucătorii pe care îi
urmăreşte. Informaţiile au scop motivaţional, acestea făcând referire la terminarea unei misiuni,
creşterea în nivel, rank, etc.
Figura 26 Pagina de știri
44
6.4 Clasamentul
În clasament se regăsesc utlizatorii sortaţi în ordinea rankului, nivelului şi a punctelor de
experienţă. Pagina reprezintă unul din punctele cheie al conceptului de gamification.
Figura 27 Pagina de clasament
6.5 Pagina de profil
Pagina de profil afişează toate informaţiile asociate unui cont. Aici, jucătorul poate
observa cine îl urmăreşte şi pe cine urmăreşte el, având posibilitatea şi să îşi editeze profilul.
Figura 28 Pagina de profil
45
6.6 Pagina administratorului
Această pagină presupune accesul autorizat. Prezintă ceilalți administratori, utilizatorii şi
misiunile, administratorul autentificat, având posibilitatea să modifice aceste date. De asemenea,
tot din această pagină se pot adauga misiunile.
Trebuie menţionat faptul că datele afişate sunt paginate. Cererea de date corespunzătoare
unei pagini se realizează printr-un apel AJAX către server, acesta returnând o listă de obiecte.
Orice ştergere din listă va actualiza automat pagina, fără a o mai încarca din nou. Înainte de
efectuarea unei ștergeri, administratorul este nevoit să își confirme acțiunea printr-o fereastra
modală.
Figura 29 Pagina administratorului
Un caz mai interesant îl reprezintă adăugarea misiunii de localizare. Aceasta specifică
posibilitatea încărcării unei imagini, setarea descrierii/dificultăţii/informaţiilor adiţionale, precum
şi alegerea coordonatelor. În momentul poziţionării unui marcaj pe hartă, automat sunt extrase
latitudinea şi longitudinea. Infomaţiile adiţionale sunt afişate jucatorului după ce acesta a
terminat cu succes misiunea. Acestea au fost introduse din scop pur informativ, pentru a oferi
detalii despre locaţia identificată.
46
Figura 30 Adăugarea unei misiuni de localizare
6.7 Prietenii
În pagina dedicată acestui subiect, jucătorul poate vedea toţi prietenii săi (persoanele pe
care le urmăreşte), împreună cu detalii aferente progresului lor (rangul, nivelul şi punctele de
experiență). De asemenea este disponibilă căutarea de utilizatori noi. Aceasta beneficiază de
autocompletare. Căutarea se realizează pentru minim 2 caractere, având totodată şi o intârziere,
pentru a nu face cereri inutile către server.
47
Figure 31 Pagina prietenilor
7. Directii viitoare ale aplicatiei
7.1 Spring Security
În momentul de faţă, controlul accesului se realizează prin rutare, fiecare cerere fiind
verificată de un interceptor (a se vedea capitolul 3.8). O altă soluţie ar fi utilizarea pachetului
Security din cadrul bibliotecii Spring. Aceasta asigură autentificarea şi autorizarea în aplicaţiile
Java. Puterea acestei biblioteci constă în uşurinţa cu care poate fi extinsă pentru a satisface
cerinţe particulare.
Avantajele ar fi:
ușurința cu care se realizează controlul accesului. Spre exemplu:
@PreAuthorize("hasRole('ROLE_ADMIN')")
public void deleteUser(String username);
vine cu pachete pentru criptarea parolei precum (Md5PasswordEncoder,
BaseDigestPasswordEncoder, etc)
are suport pentru controlul accesului chiar şi în JSP-uri
<%@ taglib prefix="sec"
uri="http://www.springframework.org/security/tags" %>
<sec:authorize access="hasRole('ROLE_ADMIN')">
Acest mesaj va fi vizibil doar pentru admini.
</sec:authorize>
48
poate expirma condiţia ca autentificarea să se realizeze doar prin HTTPS astfel în fişierul
de configurare
<http auto-config="true" use-expressions="true">
<intercept-url pattern="/login" requires-channel="https"/>
<!-- Other attributes and elements omitted -->
</https>
7.2 Chat între utilizatori
Dat fiind faptul că utilizatorii primesc ştiri (news feed) de la prietenii lor, o
funcţionalitate bună de adăugat ar fi aceea de a da posibilitatea jucătorilor să comunice live.
Acest lucru se poate realiza prin intermediul Web Socket-urilor.
Websocket-urile sunt o tehnologie modernă ce face posibilă comunicarea interactivă între
browser şi server, utilizând o singură conexiune TCP. Cu acest API se pot trimite mesaje la
server şi primi răspunsuri fără a fi aşteptate sau cerute.
Desigur, pentru a păstra mesajele, este nevoie de un model pentru baza de date. Cel de
mai jos este inspirat din structura Facebook-ului de stocare de mesaje:
Figura 32 Model de stocare a mesajelor
7.3 Transformarea aplicației într-un serviciu REST
Într-o arhiterctura REST, totul reprezintă resurse. O astfel de arhitectură ajută foarte mult
în minimizarea cuplajului dintre client şi componentele serverului.
Trecerea pe o arhitectură REST implică schimbări majore, însă ar aduce avantaje
notabile, precum posibilitatea utlizarii aceluiaşi server pe diferite tipuri de clienţi (browser,
49
aplicaţii mobile). Deoarece JSP-uri nu pot fi folosite în aplicaţii pure REST, o sugestie pentru a
realiza clientul de tip browser ar fi utilizarea bibliotecii AngularJS. Aceasta, folosită de regulă în
dezvoltarea aplicaţiilor web cu o singură pagină, se bazează pe o arhitectură MVC şi MVVM
(Model View-View Model). Avantajele ar fi ca AngularJS extinde HTML-ul, oferă suport pentru
legarea de date („data-binding”), permite testare la nivel enterprise, etc.
O altă direcţie a clientului o reprezintă aplicaţiile native pe diferite platforme (Android,
iOS, windows). Acestea pot profita de avantajele oferite de sistemul de operare, precum diferiţi
senzori, performanţă ridicată, etc. Spre exemplu, intr-o aplicație Android, aplicație ar rula mai
fluent decât in browser, localizarea s-ar face mai eficient, procesarea imaginilor s-ar putea realiza
direct pe client pentru a evita suprasolicitarea serverului, etc.
50
Concluzii
Aplicația descrisă în această lucrare propune o altă abordare a jocului de
Treasure Hunt, una automatizată, cu orientare spre obiective culturale. Consider că
este foarte important ca un ieșean să cunoască istoria și cultura propriului oraș, așa
că am decis dezvoltarea unei aplicații ce îmbină utlilul (asimilarea de noi
cunoștințe) cu plăcutul (jocul propriu-zis).
Jucătorul poate afla noi informații prin intermediul misiunilor disponibile.
Am considerat că terminarea acestor misiuni nu reprezintă o sursă de motivație
suficientă, așa că am adoptat conceptul de gamification. Practic, orice reușită este
recompensată cu puncte de experiență. Se poate spune că jucătorii sunt într-o
oarecare competiție, fiind realizat un clasament în acest sens. O problemă pe care
am ridicat-o (în misiunile de localizare) a fost necesitatea deplasării jucatorului la
edificiul sugerat. Acest lucru se bazează pe faptul că am observat cum tot mai
multă lume preferă să-și petreacă mare parte din timp izolați, în fața calculatorului
din casă. Pe lângă acest lucru, sunt de părere ca vizionarea pozei unui edificiu nu
este la fel de memorabilă precum analizarea acestuia „la fața locului”. În acest
scop, misiunile pot fi extinse pentru a realiza chiar un tur de prezentare al orașului.
În implementarea aplicației m-am orientat spre tehnologii robuste, de
actualitate. Așadar am decis dezvoltarea unei aplicații web în limbajul Java, cu
ajutorul bibliotecii Spring MVC. Am preferat o aplicație web deorece este
independentă de platforma clientului, fiind necesar doar un browser. S-au adoptat
diferite strategii de îmbunătățire a timpului de încarcare a paginii precum
solicitarea deasă a memoriei Heap16, procesarea asincronă a imaginilor, etc.
Așadar, subiectul acestei lucrări prezintă o idee nouă, ce consider ca poate
avea un impact bun în randul ieșenilor. Aplicația (ce poate avea și caracter turistic)
îndeamnă utilizatorul să iasă din casă și să se distreze în timp ce învață cultura
orașului.
16 heap – spațiul de stocare al obiectelor Java
51
Bibliografie
[1] Craig, Walls. Spring in Action. Manning, 2014.
[2] Pivotal Software. Spring Framework -
https://spring.io/docs
[3] Cameron McKenzie. Hibernate made easy. PulpJava, 2008.
[4] Apache Software Foundation. Maven -
http://maven.apache.org/guides
[5] Apache Software Foundation. Tomcat -
https://tomcat.apache.org/tomcat-7.0-doc/index.html
[6] Materialize CSS -
http://materializecss.com/about.html
[7] jQuery - http://api.jquery.com/
[8] Cristian, Frăsinaru. Curs practic de Java
http://web.info.uvt.ro/~iordan/P_III/Cristian_Frasinaru-Curs_practic_de_Java.pdf
[9] Guoshen Yu, Jean-Michel Morel. ASIFT: An algorithm for Fully Affine Invariant Comparison
http://www.ipol.im/pub/art/2011/my-asift/article_lr.pdf
[10] Brian, Burke. Gamify: How Gamification Motivates People to Do Extraordinary Things. Hardcover,
2014.
[11]Robert, Martin. Clean Code: A Handbook of Agile Software Craftmanship. Prentice Hall, 2009.
[12] StackOverflow – http://stackoverflow.com/