Post on 29-Sep-2020
Facultade de Informatica
Departamento de Computacion
Trabajo Fin de Master
Mestrado Universitario en Enxenarıa Informatica
Plataforma escalable para el almacenamiento,publicacion y busqueda avanzada de fotografıas
Autor: David Albela Perez
Director: Oscar Pedreira Fernandez
A Coruna, septiembre de 2015
Informacion general
Tıtulo del proyecto: Plataforma escalable para el almace-
namiento, publicacion y busqueda avanzada
de fotografıas.
Clase de proyecto: Proyecto clasico de ingenierıa
Nombre del autor : David Albela Perez
Nombre del director : Oscar Pedreira Fernandez
Miembros del tribunal :
Miembros suplentes :
Fecha de lectura:
Calificacion:
ii
A mis padres.
iv
AGRADECIMIENTOS
En primer lugar, quisiera agradecer a mis padres por todo el apoyo que me han dado.
A Monica por ser tan especial para mı y estar siempre a mi lado.
A mi director y amigo Oscar Pedreira Fernandez, por su gran ayuda y consejos
en la realizacion de este trabajo.
A todos mis companeros de facultad y del master. A Cristian, Jose, Juan,
Adrian, Abrahan y Eva. Gracias por compartir esas ganas de aprender mas.
Gracias a todos.
v
vi
RESUMEN
El almacenamiento y recuperacion de imagenes a partir de sus metadatos o contenido
impone en general una alta demanda de recursos computacionales, tanto desde el
punto de vista del almacenamiento como del procesado. Este problema se agrava
cuando las colecciones de imagenes contienen miles o millones de elementos. En este
contexto, para obtener un tiempo de respuesta adecuado, es necesario el diseno de
soluciones extensibles y escalables.
Este trabajo de fin de master se desarrolla con el objetivo de proveer un sistema
escalable para el almacenamiento y la recuperacion de imagenes junto con un motor
de busqueda avanzado para los metadatos y el contenido de la imagen.
Ademas, la aplicacion incluye una API en forma de servicio REST completo para
su integracion en aplicaciones de terceros. Una interfaz web que realiza llamadas
contra esta API muestra de forma general el sistema de la aplicacion. Durante
el desarrollo se sigue el uso de buenas practicas mediante patrones de diseno y
metodologıas agiles como Scrum. La aplicacion busca lograr un sistema reactivo,
preparado para las exigencias de los usuarios y de las aplicaciones de hoy en dıa,
mediante bases de datos NoSQL como Cassandra y frameworks como Spring Boot
y AngularJs.
Para el control de versiones se ha utilizado un repositorio Git en el sitio web
Bitbucket. La memoria del proyecto ha sido realizada en Latex con la herramienta
TexStudio.
Palabras clave: fotografıa, cbir, reverse image search, retrieve pictures, meta-
data, lire, cassandra, datastax, lucene, solr, restful, jhipster, spring boot, angularjs
vii
viii
INDICE GENERAL
1 INTRODUCCION 1
1.1 Motivacion. . . . . . . . . . . . . . . . . . . . . . . . . 1
1.2 Objetivos . . . . . . . . . . . . . . . . . . . . . . . . . 2
1.3 Estructura del trabajo . . . . . . . . . . . . . . . . . . . . 3
2 ESTADO DE LA CUESTION 5
2.1 Introduccion . . . . . . . . . . . . . . . . . . . . . . . . 5
2.2 Caliph & Emir . . . . . . . . . . . . . . . . . . . . . . . 6
2.3 Google Image Search . . . . . . . . . . . . . . . . . . . . 8
2.4 TinEye . . . . . . . . . . . . . . . . . . . . . . . . . . 9
2.5 ImageRaider . . . . . . . . . . . . . . . . . . . . . . . . 10
3 FUNDAMENTOS TECNOLOGICOS 13
3.1 Introduccion . . . . . . . . . . . . . . . . . . . . . . . . 13
3.2 Apache Cassandra . . . . . . . . . . . . . . . . . . . . . 14
3.2.1 Pruebas de Rendimiento . . . . . . . . . . . . . . . . 15
3.2.2 Hadoop vs Cassandra. . . . . . . . . . . . . . . . . . 15
3.3 Solr . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
3.3.1 DataStax . . . . . . . . . . . . . . . . . . . . . . . 17
3.4 LIRe . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
3.4.1 Descriptores visuales . . . . . . . . . . . . . . . . . . 20
3.4.2 Indexacion y Busqueda de Imagenes en Lucene . . . . . . . 22
ix
x INDICE GENERAL
3.5 JHipster . . . . . . . . . . . . . . . . . . . . . . . . . . 23
3.5.1 Aplicacion Lado Servidor o Back-End . . . . . . . . . . . 24
3.5.2 Aplicacion Lado Cliente o Front-End . . . . . . . . . . . 26
3.5.3 Creacion de un proyecto en Cassandra con JHipster. . . . . 29
3.5.4 Problemas y Bugs encontrados . . . . . . . . . . . . . . 29
3.5.5 Instalacion de Cassandra y Ejecucion en desarrollo . . . . . 30
4 METODOLOGIA DE DESARROLLO 37
4.1 Introduccion . . . . . . . . . . . . . . . . . . . . . . . . 37
4.2 Scrum. . . . . . . . . . . . . . . . . . . . . . . . . . . 38
4.3 Adaptacion al proyecto . . . . . . . . . . . . . . . . . . . 41
4.3.1 Analisis . . . . . . . . . . . . . . . . . . . . . . . 41
4.3.2 Diseno . . . . . . . . . . . . . . . . . . . . . . . . 42
5 PLANIFICACION Y SEGUIMIENTO 43
5.1 Tareas y recursos . . . . . . . . . . . . . . . . . . . . . . 43
5.2 Planificacion inicial y coste . . . . . . . . . . . . . . . . . . 47
5.3 Seguimiento . . . . . . . . . . . . . . . . . . . . . . . . 49
6 ANALISIS 51
6.1 Usuarios . . . . . . . . . . . . . . . . . . . . . . . . . . 51
6.2 Imagenes . . . . . . . . . . . . . . . . . . . . . . . . . 53
6.3 Imagenes de Usuario . . . . . . . . . . . . . . . . . . . . 54
6.4 Metadatos . . . . . . . . . . . . . . . . . . . . . . . . . 56
6.5 Busqueda Avanzada de Imagenes . . . . . . . . . . . . . . . 56
6.6 Administracion de imagenes . . . . . . . . . . . . . . . . . 58
7 DISENO 61
7.1 Arquitectura del sistema . . . . . . . . . . . . . . . . . . . 61
7.1.1 Cassandra DataStax Enterprise . . . . . . . . . . . . . 65
7.1.2 Arquitectura REST . . . . . . . . . . . . . . . . . . 67
INDICE GENERAL xi
7.2 Modelo Logico de Datos . . . . . . . . . . . . . . . . . . . 67
7.3 Diseno de la Aplicacion . . . . . . . . . . . . . . . . . . . 70
7.3.1 Modelo . . . . . . . . . . . . . . . . . . . . . . . . 70
7.3.2 API Rest . . . . . . . . . . . . . . . . . . . . . . . 76
7.3.3 Aplicacion Lado Cliente o Front-End . . . . . . . . . . . 78
8 IMPLEMENTACION Y PRUEBAS 79
8.1 Implementacion. . . . . . . . . . . . . . . . . . . . . . . 79
8.1.1 Arquitectura Global de Paquetes . . . . . . . . . . . . . 79
8.1.2 Cassandra y Solr. . . . . . . . . . . . . . . . . . . . 79
8.1.3 Modelo . . . . . . . . . . . . . . . . . . . . . . . . 84
8.1.4 Servicios . . . . . . . . . . . . . . . . . . . . . . . 90
8.1.5 Controlador y Servicio Web . . . . . . . . . . . . . . . 98
8.1.6 Aplicacion Lado Cliente o Front-End . . . . . . . . . . . 106
8.2 Pruebas . . . . . . . . . . . . . . . . . . . . . . . . . . 109
8.2.1 Pruebas Unitarias . . . . . . . . . . . . . . . . . . . 109
8.2.2 Pruebas de Integracion . . . . . . . . . . . . . . . . . 110
8.2.3 Pruebas de Estres . . . . . . . . . . . . . . . . . . . 111
8.2.4 Pruebas de Compatibilidad . . . . . . . . . . . . . . . 114
8.2.5 Pruebas de Aceptacion . . . . . . . . . . . . . . . . . 114
8.3 Herramientas utilizadas . . . . . . . . . . . . . . . . . . . 116
8.3.1 Cassandra Community Edition. . . . . . . . . . . . . . 116
8.3.2 DataStax Enterprise Edition. . . . . . . . . . . . . . . 116
8.3.3 Draw.io . . . . . . . . . . . . . . . . . . . . . . . 116
8.3.4 UML. . . . . . . . . . . . . . . . . . . . . . . . . 116
8.3.5 ProjectLibre . . . . . . . . . . . . . . . . . . . . . 117
8.3.6 IntelliJ Idea . . . . . . . . . . . . . . . . . . . . . . 117
8.3.7 Git . . . . . . . . . . . . . . . . . . . . . . . . . 117
9 SOLUCION DESARROLLADA 119
9.1 Introduccion . . . . . . . . . . . . . . . . . . . . . . . . 119
xii INDICE GENERAL
9.2 API. . . . . . . . . . . . . . . . . . . . . . . . . . . . 119
9.2.1 Recursos del Usuario . . . . . . . . . . . . . . . . . . 122
9.2.2 Recursos de Imagenes . . . . . . . . . . . . . . . . . 122
9.2.3 Recursos de Metadatos . . . . . . . . . . . . . . . . . 122
9.2.4 Recursos de Busquedas por contenido . . . . . . . . . . . 123
9.3 Interfaz de la aplicacion . . . . . . . . . . . . . . . . . . . 123
9.3.1 Pagina de inicio . . . . . . . . . . . . . . . . . . . . 123
9.3.2 Usuarios . . . . . . . . . . . . . . . . . . . . . . . 123
9.3.3 Imagenes . . . . . . . . . . . . . . . . . . . . . . . 124
9.3.4 Almacenar Imagenes del usuario . . . . . . . . . . . . . 124
9.3.5 Metadatos . . . . . . . . . . . . . . . . . . . . . . 124
9.3.6 Busquedas por imagenes . . . . . . . . . . . . . . . . 124
10 CONCLUSIONES Y TRABAJO FUTURO 141
10.1 Conclusiones . . . . . . . . . . . . . . . . . . . . . . . . 141
10.2 Trabajo futuro . . . . . . . . . . . . . . . . . . . . . . . 143
BIBLIOGRAFIA 145
MANUAL DE INSTALACION 151
.1 Instalacion paquetes basicos . . . . . . . . . . . . . . . . . 151
INDICE DE FIGURAS
1.1 Caracterısticas de los sistemas reactivos. . . . . . . . . . . . . . . . . 3
2.1 Sistema de recuperacion de imagenes basada en puntuacion. . . . . . 6
2.2 Sistema de recuperacion basado en aprendizaje automatico. . . . . . . 7
2.3 Demo de resultados en aplicacion Emir. . . . . . . . . . . . . . . . . . 8
2.4 Resultados de Google de imagen de goleta. . . . . . . . . . . . . . . . 9
2.5 Resultados de Google imagen de la Torre. . . . . . . . . . . . . . . . 10
2.6 Resultados de Google de imagen de chip. . . . . . . . . . . . . . . . . 11
2.7 Busqueda avanzadas en Google . . . . . . . . . . . . . . . . . . . . . 11
2.8 Resultados de busqueda en TinEye. . . . . . . . . . . . . . . . . . . . 12
3.1 Topologıa de cluster de Cassandra . . . . . . . . . . . . . . . . . . . . 14
3.2 Pruebas de rendimiento de Cassandra, HBase, MongoDBy y CouchDB 16
3.3 Pruebas de Rendimiento de Cassandra por Netflix . . . . . . . . . . . 32
3.4 Funcionalidades del producto DataStax Enterprise . . . . . . . . . . . 33
3.5 Logo del proyecto Lire. . . . . . . . . . . . . . . . . . . . . . . . . . . 33
3.6 Ejemplo de descriptor de caracterısticas PHOG. . . . . . . . . . . . . 34
3.7 JHipster crea proyectos web con Spring Boot y AngularJs. . . . . . . 34
3.8 Spring Boot automatiza la construccion y configuracion de proyectos
en Spring. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
3.9 MMVC en AngularJs. . . . . . . . . . . . . . . . . . . . . . . . . . . 35
3.10 Enlace doble de los datos en Angular. . . . . . . . . . . . . . . . . . . 36
4.1 Marco de Trabajo Scrum. . . . . . . . . . . . . . . . . . . . . . . . . 38
4.2 Proceso de desarrollo con Scrum. . . . . . . . . . . . . . . . . . . . . 39
xiii
xiv INDICE DE FIGURAS
5.1 Diagrama de Gantt con planificacion de las iteraciones. . . . . . . . . 48
6.1 Caso de uso de usuarios . . . . . . . . . . . . . . . . . . . . . . . . . 52
6.2 Caso de uso de imagenes . . . . . . . . . . . . . . . . . . . . . . . . . 54
6.3 Caso de uso de mis imagenes . . . . . . . . . . . . . . . . . . . . . . . 55
6.4 Caso de uso de mis imagenes . . . . . . . . . . . . . . . . . . . . . . . 56
6.5 Caso de uso busqueda avanzada de imagenes . . . . . . . . . . . . . . 57
6.6 Caso de uso de administracion de imagenes . . . . . . . . . . . . . . . 59
7.1 Arquitectura Global de la aplicacion. . . . . . . . . . . . . . . . . . . 64
7.2 Arquitectura de segregacion de nodos en DataStax. . . . . . . . . . . 65
7.3 Arquitectura DSE Search con Cassandra y Solr . . . . . . . . . . . . 66
7.4 Niveles de madurez del servicio REST [Fowler, 2010]. . . . . . . . . . 68
7.5 Diagrama de clases en UML. . . . . . . . . . . . . . . . . . . . . . . . 72
7.6 Diagrama de la Capa de Acceso a Datos. . . . . . . . . . . . . . . . . 73
7.7 Diagrama de la Implementacion de los Servicios. . . . . . . . . . . . . 74
7.8 Diagrama de Secuencia Almacenar Imagen . . . . . . . . . . . . . . . 76
7.9 Diagrama de Secuencia Busqueda de Imagenes . . . . . . . . . . . . . 77
8.1 Arquitectura global de paquetes de la aplicacion. . . . . . . . . . . . . 80
8.2 Panel de administracion de Solr. . . . . . . . . . . . . . . . . . . . . . 84
9.1 API - Creacion de token de accesso. . . . . . . . . . . . . . . . . . . . 123
9.2 API - Servicio de usuarios. . . . . . . . . . . . . . . . . . . . . . . . . 124
9.3 API - Recurso de imagenes. . . . . . . . . . . . . . . . . . . . . . . . 125
9.4 API - Recurso de imagenes de usuarios. . . . . . . . . . . . . . . . . . 126
9.5 API - Recurso de Metadatos. . . . . . . . . . . . . . . . . . . . . . . 127
9.6 API - Recurso para las busquedas por contenido de imagen. . . . . . 128
9.7 API - Recurso de resultados de las busquedas. . . . . . . . . . . . . . 129
9.8 Pagina de inicio de la aplicacion. . . . . . . . . . . . . . . . . . . . . 130
9.9 Interfaz - Login de usario. . . . . . . . . . . . . . . . . . . . . . . . . 130
9.10 Interfaz - Registro de usuario. . . . . . . . . . . . . . . . . . . . . . . 131
9.11 Interfaz - Listado de imagenes. . . . . . . . . . . . . . . . . . . . . . . 131
9.12 Interfaz - Busqueda de imagenes por texto. . . . . . . . . . . . . . . . 132
9.13 Interfaz - Subir imagen. . . . . . . . . . . . . . . . . . . . . . . . . . 132
INDICE DE FIGURAS xv
9.14 Interfaz - Listado de imagenes de usuario. . . . . . . . . . . . . . . . 133
9.15 Interfaz - Editar imagenes de usuario. . . . . . . . . . . . . . . . . . . 133
9.16 Interfaz - Eliminar imagen de usuario. . . . . . . . . . . . . . . . . . 134
9.17 Interfaz - Listado de metadatos. . . . . . . . . . . . . . . . . . . . . . 134
9.18 Interfaz - Busqueda de metadatos. . . . . . . . . . . . . . . . . . . . . 135
9.19 Busqueda por contenido de imagenes. . . . . . . . . . . . . . . . . . . 135
9.20 Busqueda de imagen de chip. . . . . . . . . . . . . . . . . . . . . . . 136
9.21 Resultados de busqueda de chip. . . . . . . . . . . . . . . . . . . . . . 137
9.22 Busqueda de goleta. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137
9.23 Resultados de busqueda de goleta. . . . . . . . . . . . . . . . . . . . . 138
9.24 Resultados de busqueda de goleta. . . . . . . . . . . . . . . . . . . . . 139
9.25 Resultados de busqueda de goleta. . . . . . . . . . . . . . . . . . . . . 140
xvi INDICE DE FIGURAS
INDICE DE TABLAS
7.1 Tabla de Usuarios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69
7.2 Tabla de Imagenes . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69
7.3 Tabla de Metadatos . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70
7.4 Tabla de Imagenes Buscadas . . . . . . . . . . . . . . . . . . . . . . . 70
7.5 Tabla de Imagenes Encontradas . . . . . . . . . . . . . . . . . . . . . 71
xvii
xviii INDICE DE TABLAS
1INTRODUCCION
1.1 MOTIVACION
Los avances de contenidos multimedia han producido una enorme cantidad de archi-
vos de imagenes digitales para una gran variedad de dominios de aplicacion desde
periodismo y fotografıa profesional a redes sociales, deportes y ocio. Toda esta infor-
macion es util solo cuando se puede acceder a ella de manera eficiente y determinar
a que categorıa corresponde.
Es habitual ademas que las aplicaciones web de publicacion y almacenamiento de
imagenes permitan anadir datos adicionales como un tıtulo de imagen, descripcion,
“hashtags” y categorıas. Los metadatos incluidos en las imagenes como la marca de
la camara, fecha de captura o la posicion geografica ayudan a entender y clasificar
los datos multimedia. Pero no siempre toda la informacion queda bien descrita, es
susceptible a errores y no suele ser suficiente para la busqueda y recuperacion de
contenidos multimedia. La clasificacion automatica de una fotografıa, su busqueda
1
2 Capıtulo 1
o determinar quienes estan presentes es uno de los objetivos de este trabajo, por lo
tanto, las exigencias para la comprension efectiva, anotacion y recuperacion de la
informacion visual estan creciendo en todo el mundo.
1.2 OBJETIVOS
Como se describe en la seccion anterior, el principal objetivo del trabajo es el diseno
e implementacion de una herramienta capaz de almacenar y recuperar imagenes a
traves de su propio contenido. Este desarrollo incluye una arquitectura web lista para
ser desplegada en un servidor de aplicaciones, capaz de responder a las necesidades de
respuesta de los usuarios. Este objetivo general se detalla en los siguientes objetivos
particulares:
• Proveer un servicio de almacenamiento para imagenes, escalable y tolerante a
fallos. Capaz de soportar gran cantidad de peticiones y datos.
• Busquedas avanzadas: recuperacion de imagenes basada en contenido y de
informacion basada en los metadatos.
• API para que terceros, sobre una arquitectura REST completa.
• Sistemas de acceso y de autenticacion a la API e interfaz.
• Interfaz web sencilla bajo un front-end, independiente de la arquitectura del
lado servidor.
Ademas de las caracterısticas directamente relacionadas con el producto desarro-
llado, el trabajo incluye tareas de planificacion y gestion, analisis, diseno y pruebas
de la aplicacion.
Sistema Reactivo. Hoy en dıa las aplicaciones son desplegadas en cualquier in-
fraestructura o dispositivo, desde moviles y microordenadores hasta servicios Cloud
en miles de procesadores multinucleo. Ademas, los usuarios son cada vez mas exigen-
tes esperan tiempos de respuesta de milisegundos y que sus sistemas operen 24/7. El
desarrollo de servicios web bajo arquitecturas como REST son indispensables para
Introduccion 3
la creacion de aplicaciones “middleware” ya que cada vez se extiende mas el uso del
usuario “smartphone”.
Por ello es necesario uso de buenas practicas y patrones de diseno software junto
con las ultimas tecnologıas de desarrollo de aplicaciones web y que incluya las carac-
terısticas descritas en el “Manifiesto de Sistemas Reactivos”[Boner, Jonas et al., 2013].
Los Sistemas construidos como Sistemas Reactivos son mas flexibles, con bajo
acoplamiento y escalables 1.1
Figura 1.1: Caracterısticas de los sistemas reactivos.
1.3 ESTRUCTURA DEL TRABAJO
La memoria se estructura en varios capıtulos que se detallan a continuacion:
• Introduccion: Capıtulo en el que se explica la motivacion, los objetivos que
persigue el proyecto y la estructura del trabajo.
• Estado de la cuestion: Aquı se se hace un repaso de herramientas similares
del mercado actual, el analisis de la funcionalidad que proporcionan.
• Fundamentos Tecnologicos: En este apartado se describen las tecnologıas
utilizadas como Spring Boot y AngularJs; generado inicialmente con el auto-
generador de Yeoman JHipster; la base de datos NoSQL Cassadra con el driver
para Java de DataStax y su integracion en Solr.
• Metodologıa de desarrollo: Se explica la metodologıa para el desarrollo
agil, iterativo e incremental Scrum.
• Planificacion y seguimiento: La descripcion de las tareas que se han lle-
vado a cabo para realizar este proyecto y de los recursos humanos y tecnicos
empleados para hacerlo.
4 Capıtulo 1
• Analisis y Diseno: En este capıtulo se describen los requisitos previos para
realizar la aplicacion, los diagramas de casos de uso junto con el diseno de
la arquitectura del sistema y los diferentes elementos que integra DataStax
Enterprise Edition.
• Implementacion y pruebas: Detalles de implementacion de las funciona-
lidades mas importantes de la aplicacion, incluyendo parte del codigo de las
mismas. Ademas se describen los tipos de pruebas a los que se ha sometido y
las herramientas utilizadas para la realizacion del proyecto.
• Solucion desarrollada: Documentacion detallada paso a paso como con cada
una de las partes de la aplicacion, mostrando capturas de pantalla de la misma.
• Conclusiones y trabajo futuro: Descripcion de como se han alcanzado
los objetivos y de las caracterısticas mas importantes del trabajo realizado.
Ademas de ampliaciones posibles que pueden ser interesantes aplicar en el
futuro.
2ESTADO DE LA CUESTION
2.1 INTRODUCCION
El uso de tecnologıas para el almacenamiento de imagenes en la nube o servicios
Cloud cada vez son mas utilizados, es habitual que los usuarios almacenen sus fo-
tografıas a traves de smartphones y que sus imagenes sean clasificadas mediante
anotaciones “hashtag”. Estas practicas y la cada vez mayor exigencia de los usua-
rios a la hora de utilizar dejan claro que los sistemas de almacenamiento multimedia
deben ser escalables y tolerantes a fallos.
Los metadatos incluyen informacion como la fecha de exposicion, marca del dis-
positivo, parametros de captura de imagen o posicion de la captura. Estos datos se
incluyen en las cabeceras de las imagenes digitales, “EXIF” (Exchangeable Image
Format) es un estandar para metadatos de imagenes. Estos metadatos se utilizan
habitualmente para ayudar a organizar y catalogar adecuadamente las imagenes.
La recuperacion de imagenes por contenido o CBIR (Content Base Images Re-
5
6 Capıtulo 2
trieval) es un campo de la ciencia de la computacion que trata de obtener imagenes
similares como si un humano determinase su similitud o clasificacion. Esta recu-
peracion suele ser un proceso costoso, en primer lugar por la extraccion de las ca-
racterısticas y en segundo lugar por la necesidad de algoritmos de indexacion o de
aprendizaje automatico.
En las figuras 2.1 y 2.2 se muestran ejemplos del proceso de recuperacion de
imagenes basados en puntuacion y aprendizaje respectivamente
[Qi, Xiaojun and Allan, Steve, 2007].
Figura 2.1: Sistema de recuperacion de imagenes basada en puntuacion.
A continuacion se describen algunas de las aplicaciones para la busqueda por
imagenes.
2.2 CALIPH & EMIR
Caliph y Emir son prototipos escritos en Java para la recuperacion de imagenes en
fotografıa digital. Las aplicaciones para escritorio sirven como anotacion de metada-
Estado de la cuestion 7
Figura 2.2: Sistema de recuperacion basado en aprendizaje automatico.
tos semanticos y del contenido de recuperacion de imagenes basada en descriptores
del estandar MPEG-7. Ademas de la lectura de la informacion existente, se realiza la
transformacion de esta informacion a MPEG-7. La informacion semantica de la ima-
gen se presenta como grafo dirigido, donde los nodos reflejan objetos semanticos que
definen las relaciones entre las imagenes. Los metadatos, para mejorar la eficiencia
de recuperacion del contenido, tambien son extraıdos. Para una visualizacion mas
rapida, se escala el tamano de imagenes en forma de miniaturas. Los MPEG-7 cons-
tan de las siguientes partes: descripcion de metadatos, fecha de creacion, informacion
de medios, anotacion textual, semantica y descriptores visuales.
Caliph & Emir es desarrollada por Mathias Lux y otros autores. A partir de esta
herramienta, crean posteriormente la librerıa de software libre Lire
[Lux, Mathias and Chatzichristofis, Savvas A., 2015a] basada en los trabajos de
[Lux, Mathias and Chatzichristofis, Savvas A., 2008] y [Lux, 2011].
8 Capıtulo 2
Figura 2.3: Demo de resultados en aplicacion Emir.
2.3 GOOGLE IMAGE SEARCH
Entre todas las herramientas online de recuperacion de imagenes, la aplicacion de
Google [Google, 2005] es la mas destacable dado que contiene una gran cantidad de
imagenes almacenadas e indexadas, ademas de suficientes recursos
Para cargar una imagen a buscar, se puede hacer clic en el icono de la camara
de la barra de busqueda. Se pedira subir una imagen desde el ordenador o pegar un
enlace de imagen directamente. Como alternativa, se puede arrastrar y soltar una
imagen desde el ordenador al logo de la imagen de Google.
No solo realiza busqueda a traves de caracterısticas extraıdas de las imagenes,
tambien realiza comparacion de los valores de los metadatos. Las imagenes envia-
das y los resultados de busqueda son almacenados por Google para mejoras en las
busquedas. Algunas de las busquedas realizadas a modo de pruebas se muestran
a continuacion. En la imagen de la goleta 2.4 y la placa con el chip 2.6 se puede
apreciar un resultado de busqueda optimo. Sin embargo, en la busqueda del paisaje
de cometas con la Torre de Hercules y la estatua de Breogan 2.5 de fondo, no se
encuentra ningun parecido exceptuando los colores del paisaje. Tambien permite
filtar las opciones de busqueda por color, tipo de imagen (paisaje, cara, fotografıa,
dibujo lineal y animadas), fecha y licencias de uso.
Estado de la cuestion 9
Figura 2.4: Resultados de Google de imagen de goleta.
2.4 TINEYE
TinEye [TinEye, 2005] tambien realiza la busqueda de imagenes de forma casi tan
precisa como en Google, exceptuando la cantidad de resultados que puede ofrecer.
Esto podrıa deberse a que las imagenes de Google estan indexadas sobre su gran
base de datos de mas de dos mil millones de imagenes. Sin embargo, TinEye anade
algo que Google no ofrece, una API en forma de servicio web REST.
Por esa razon, empresas como Ebay o Istockphoto 1 han recurrido a la solucion
de TinEye para integrar su tecnologıa en sus aplicaciones.
TinEye se puede utilizar para fines no comerciales de forma gratuita. Para uso
comercial, su API incluye precios anuales desde 200 $ hasta los 10.000 $. Los resul-
tados de las busquedas son almacenados se puedes volver a recuperar ası como ver
una lista de las mas populares. La API para realizar busquedas es un servicio web
REST con interfaz en formato de texto JSON.
Durante las pruebas de busquedas, con las imagenes utilizadas en las pruebas de
Google no se obtuvieron resultados, seguramente porque no eran imagenes compar-
tidas en Internet. Sin embargo, sı se obtuvieron exito en imagenes muy similares a
existentes en Internet, como por ejemplo la imagen de Lena2.8.
1http://www.istockphoto.com/
10 Capıtulo 2
Figura 2.5: Resultados de Google imagen de la Torre.
2.5 IMAGERAIDER
ImageRaider [ImageRaider, 2005] es un buscador de imagenes a traves de las dife-
rentes APIs de las redes sociales de imagenes para fotografıa y arte mas importantes,
tales como 500px o DevianArt. Permite realizar busqueda multiples, con mas de una
imagen, lo que significa que puede ejecutar multi-busquedas de imagenes Ademas,
permite realizar busquedas de todas las imagenes contenidas a traves de enlaces.
Sus servicios estan orientados a profesionales de la fotografıa, maquetadores de
prensa y posicionamiento SEO (Search Engine Optimization). Es de uso gratuito
para fines no comerciales con limitacion en el numero de busquedas. Incluye precios
para empresas con un sistema de puntos o creditos, un credito por imagen. Cada
credito tiene un coste de 1 libra por 300 creditos.
Estado de la cuestion 11
Figura 2.6: Resultados de Google de imagen de chip.
Figura 2.7: Busqueda avanzadas en Google
12 Capıtulo 2
Figura 2.8: Resultados de busqueda en TinEye.
3FUNDAMENTOS TECNOLOGICOS
3.1 INTRODUCCION
En este capıtulo se revisan las tecnologıas que se han utilizado en el proyecto. En
primer lugar se describe la base de datos NoSQL Cassandra utilizada para el desa-
rrollo de la aplicacion; para el almacenamiento escalable de imagenes; se muestran
pruebas de rendimiento realizadas y se describen las diferencias funcionales entre
un cluster de Cassandra y otro de Hadoop [Cutting, 2015b]. La solucion utilizada
para la implementacion es el producto de Cassandra de DataStax [DataStax, 2015a],
utilizado por empresas mas vanguardistas de la actualidad tales como Netflix o eBay.
A continuacion se detallan las funcionalidades de Lire
[Lux, Mathias and Chatzichristofis, Savvas A., 2015a] para la extraccion de carac-
terısticas, indexacion y posterior busqueda avanzada de imagenes.
Por ultimo se describen los componentes de la aplicacion web generada con JHips-
ter [Dubois and Mirc, 2015], creador de aplicaciones web. En este apartado se docu-
13
14 Capıtulo 3
mentan los principales frameworks que utiliza: Spring Boot [Spring, 2015b] para el
lado servidor o “back-end” y AngularJs [Google, 2015a] para la aplicacion del lado
cliente o “front-end”.
3.2 APACHE CASSANDRA
Apache Cassandra [Lakshman, Avinash and Malik, Prashant, 2010] es una base de
datos NoSQL distribuida y masivamente escalable ideal para alta velocidad de res-
puesta y transacciones de datos online. Fue desarrollada por Avinash Lakshman y
Prashant Malik, programadores de Facebook, en el 2010 y se basa en un modelo de
almacenamiento de tipo clave-valor. Esta escrita en Java y publicada bajo licencia de
software libre Apache License 2.0. Cassandra es perfecta para la gestion de grandes
cantidades de datos estructurados, semi-estructurados, y no estructurados a traves
de multiples centros de datos y la nube. Ofrece alta disponibilidad, escalabilidad li-
neal y la simplicidad operativa a traves de muchos servidores de las materias primas
sin ningun punto unico de fallo, junto con un modelo dinamico de datos de gran
alcance disenado para una maxima flexibilidad y tiempos de respuesta rapidos.
Su objetivo principal es la escalabilidad lineal y la disponibilidad. La arquitectura
distribuida de Cassandra esta basada en una serie de nodos iguales que se comuni-
can con un protocolo P2P con lo que la redundancia es maxima. Empresas como
Facebook, Twitter o Netflix utilizan Cassandra para que sus aplicaciones puedan
recibir gran cantidad de peticiones de lectura y escritura por segundo.
Figura 3.1: Topologıa de cluster de Cassandra
Los datos son almacenados en particionado de filas, con replicacion de datos en
varios nodos. Las tablas se almacenan en las denominadas “KeySpace” (analogo al
Fundamentos tecnologicos 15
esquema en bases de datos relacionales). Cada KeySpace establece la estrategia de
almacenamiento y nivel de replicacion.
Para el desarrollo de la aplicacion se utiliza la ultima version estable 2.1 y el
driver de Java 3.1 de DataStax. Los datos son almacenados en una KeySpace con
factor de replicacion tres.
3.2.1 Pruebas de Rendimiento
Cassandra es capaz de manejar petabytes de informacion y miles de usuarios de
forma concurrente.
Varias pruebas de rendimiento realizadas por la Universidad de Toronto y la
companıa Netflix [PlanetCassandra, 2015] destacan los altos rendimiento, alcanzan-
do hasta un millon de escrituras por segundo en una de las pruebas de Netflix con
mas de 256 nodos. En la figura 3.2 se puede observar una comparativa del rendi-
miento de operaciones mixta, lecturas y escrituras, entre las bases de datos NoSQL
Cassandra, HBase, MongoDB y CouchDB. Las pruebas se realizan en instancias de
Amazon Web Services con 15 GB de memoria RAM y 4 nucleos de procesador.
Las pruebas de la figura realizan las siguientes operaciones: 60% de lectura,
25% actualizaciones, 10% inserciones y 5% de escaneo. Como resultado, Cassandra
soporta entre 18.000 y 380.000 operaciones por segundo.
En la figura 3.3 se muestran los resultados obtenidos por Netflix para 48, 96, 144
y 288 nodos.
3.2.2 Hadoop vs Cassandra
El framework de Hadoop [Cutting, 2015b] esta pensado como un sistema de analisis
para Big Data enfocado en casos de uso para Data Warehousing. Ambos facilitan
una escalabilidad horizontal sencilla con uso de hardware basico.
Hadoop incluye un sistema de ficheros distribuido llamado HDFS (Hadoop Dis-
tributed File System [Shvachko, Konstantin et al., 2010]) y un motor de trabajos en
batch MapReduce. Su ventaja es que es un sistema escalable que utiliza hardwa-
re basico, commodity hardware, con soporte para alta disponibilidad y redundancia
de datos; replicando 3 bloques de datos de 128 megas. Dado el tamano de bloque,
Hadoop es ideal almacenamiento y analisis de ficheros de gran tamano.
16 Capıtulo 3
Figura 3.2: Pruebas de rendimiento de Cassandra, HBase, MongoDBy y CouchDB
Al igual que con las aplicaciones en bases de datos relacionales, por lo gene-
ral existe una necesidad importante de las webs modernas, aplicaciones moviles e
IoT 1 para tener una base de datos dedicada a la vez a operaciones online (inclu-
yendo analisis sobre datos en caliente) y un entorno de almacenamiento para fines
analıticos.
Apache Cassandra es una opcion de base de datos perfecta para aplicaciones web
y moviles online, mientras que Hadoop se dirige al tratamiento de los datos en fıo.
3.3 SOLR
Solr [Cutting, 2015a] es un motor de busquedas basado en Lucene [Apache, 2015c]
para sitios web con integracion de APIs XML/JSON, cache de datos e interfaz de
administracion. Es un buscador de alto rendimiento, escalable y tolerante a fallos.
Proporciona indexacion de forma distribuida con replicacion de los datos en varios
1Internet of Things
Fundamentos tecnologicos 17
nodos y peticion de consultas con balanceo de carga. Las funciones de busqueda
y navegacion de Solr son utilizadas por grandes companıas como Netflix para la
mejora en busquedas de aplicaciones web2.
La aplicacion web utiliza Solr para la indexacion y busquedas de los datos de las
tablas de imagenes y metadatos. Para ello se utilizan los productos de DataStax del
apartado 3.3.1.
3.3.1 DataStax
DataStax [DataStax, 2015a] es una companıa que ofrece Apache Cassandra como
plataforma de base de datos para soluciones web, movil o IoT. Distribuye productos
y aplicaciones de software libre junto con servicios de soporte, formacion y certifi-
cacion.
El modelo de negocio de DataStax se centra en la venta de una version de
Cassandra para empresas, DataStax Enterprise [DataStax, 2015c], la cual incluye
herramientas y caracterısticas adicionales para analisis, busquedas, seguridad, mo-
nitorizacion visual, gestion y soporte In-memory.
La companıa ofrece dos productos para Apache Cassandra, el paquete de software
libre DataStax Community [DataStax, 2015b] y la version de pago por subscripcion
de licencia DataStax Enterprise. La version de pago Enterprise se divide en dos
versiones: la version Standard y la version Max, con tres funcionalidades destacables:
DSE Search, DSE Analytics y DSE In-Memory.
La arquitectura que ofrece DataStax permite a cualquier usuario autorizado co-
nectarse a cualquier nodo mediante el lenguaje CQL3. CQL utiliza una sintaxis muy
similar a SQL, la forma mas basica para interactuar con Cassandra es utilizando la
herramienta de lınea de comando cqlsh. cqlsh permite crear KeySpaces, creacion y
descripcion de las tablas, insercion de datos y consultas. Incluye ademas un conjunto
de drivers para varios lenguajes de programacion.
A continuacion se describen las funcionalidades de DataStax Enterprise Max.
2https://wiki.apache.org/solr/SolrPerformanceData3Cassandra Query Language
18 Capıtulo 3
DSE Search Solr
DataStax Enterprise Search simplifica el uso de aplicaciones de busqueda para los
datos que se almacenan en una base de datos Cassandra. Utiliza el indexador y
buscador web Solr [Cutting, 2015a].
Las caracterısticas de DSE Search son:
• Una arquitectura tolerante a fallos.
• Capacidad de busqueda integrada en las tablas de Cassandra.
• Aislamiento de carga de trabajo en las busquedas de datos en la web.
• Indexacion en caliente durante la insercion de datos en Cassandra y almace-
nado automatico de los datos del ındice en Solr.
• Comandos simples para la creacion, recarga y la gestion de los recursos basicos
de Solr.
DSE Search se utiliza en la aplicacion para la indexacion de los datos almacenados
en la Cassandra y su posterior busqueda, ordenacion y paginacion de datos filtrados.
El objetivo es quitar carga de trabajo al almacenamiento
DSE Analytics Hadoop y Spark
DataStax Enterprise Anlytics incluye caracterısticas importantes para el analisis de
grandes bases de datos:
• DSE Hadoop: el framework de Hadoop es incluido en la version DataStax En-
terprise y cuenta con herramientas como Pig [Apache, 2015a] y Hive [Apache, 2015b]
para el analisis de datos en procesamiento por lotes.
• Apache Spark: un motor distribuido para procesamiento en lotes en paralelo
basado en el concepto de conjuntos de datos distribuidos resilientes (RDD), en
lugar del la solucion MapReduce en que se basa Hadoop. Alcanza rendimientos
in-memory hasta 100 veces superior al mismo obtenido con MapReduce version
1 de Hadoop.
Fundamentos tecnologicos 19
• Shark: un lenguaje para Hive que permite lanzar consultas de analisis de datos
almacenados en Cassandra.
• BYOH: soporte de integracion de la propia distribucion de Hadoop. DataStax
por defecto incluye su propio Hadoop, sin embargo, es posible que un usua-
rio desee utilizar su propio sistema de Hadoop o versiones como Cloudera y
Hortonworks.
• Apache Sqoop: herramienta para la importacion y exportacion de bases de
datos relacionales en HDFS y Cassandra.
3.4 LIRE
Lire (Lucene Image Retrieval) es una librerıa Java de codigo abierto para la extrac-
cion de caracterısticas y recuperacion de imagenes. Se basa en un motor de busqueda
de textos ligero, por lo que se puede integrar facilmente en aplicaciones sin depender
de un servidor de base de datos.
Lire utiliza caracterısticas de imagenes de contenido globales basados en la re-
cuperacion de imagenes. Proporciona los algoritmos de varios descriptores para la
extraccion de caracterısticas de imagenes para su clasificacion, algunos estandares
de MPEG-7. Los vectores obtenidos a partir de estos descriptores son indexados
en un ındice de Apache Lucene [Apache, 2015c]. Cuando se desea buscar imagenes
similares, se realiza una comparacion en el ındice de Lucene del descriptor escogido,
devolviendo una lista ordenada de imagenes con una puntuacion. Dicha puntuacion
determina el parecido de cada imagen obtenida, siendo cero el mayor parecido o
igualdad entre ambas imagenes.
Las texturas y el color son las caracterısticas mas importantes; y simples de ex-
traer; para describir objetos o imagenes. El estandar MPEG-7 tiene tres descriptores
principales relacionados con la textura.
Los descriptores utilizados en para la extraccion de caracterısticas se basan en
el color y en la textura de imagenes. Sin embargo, los resultados de las busquedas a
veces no son los esperados, especialmente en caso de que el objetivo de la busqueda
sean imagenes con figuras similares a la imagen de entrada.
20 Capıtulo 3
3.4.1 Descriptores visuales
Los descriptores visuales describen caracterısticas de imagenes y vıdeos para su re-
presentacion, indexacion y busqueda. Los descriptores son representan la conexion
digital entre los pıxeles contenidos en una imagen y lo que los humanos recorda-
mos despues de haber observado durante unos minutos una imagen. Algunas de las
aplicaciones de los descriptores son la del filtrado y control del contenido visual, cla-
sificacion de documentos. Los descriptores soportados por LIRe son de informacion
general, de color, texturas y forma.
A continuacion se describen los descriptores, algoritmos para la extraccion de
caracterısticas de imagenes, que incluye LIRe y que son utilizados en la busqueda
avanzada de imagenes de la aplicacion del trabajo de fin de master.
AutoColorCorrelogram
El descriptor AutoColorCorrelogram [Jing Huang et al., 1997] busca la relacion en-
tre el color de los puntos de una imagen para la indexacion y comparacion de image-
nes. Sus principales caracterısticas es que se basa en el color espacial HSV (siglas en
ingles de Matiz, Saturacion y Valor) y que incluye informacion sobre la correlacion
de color en la imagen.
Esta caracterıstica filtra la correlacion espacial de los colores y es a la vez eficaz y
barata para los basados en contenido de la imagen de recuperacion. El correlograma
tolera grandes cambios en la apariencia y forma de una imagen causada por cambios
en las posiciones de la camara y zoom. Se sugiere que esta caracterıstica supera no
solo el metodo tradicional basado en el histograma de color, sino que ademas en los
metodos de refinamiento del histograma recientemente propuestos para la indexacion
y recuperacion de imagenes. Proporciona una busqueda por color mucho mejor que
la de ScalableColor, pero su extraccion es algo mas lenta.
Color and Edge Directivity Descriptor
El descriptor Color and Edge Directivity Descriptor (CEDD en adelante)
[Chatzichristofis, Savvas A. and Boutalis, Yiannis S., 2008] incorpora el color y la
textura de la informacion en un histograma. El tamano se limita a 54 bytes por
Fundamentos tecnologicos 21
cada imagen, lo que lo hace adecuado para su uso en bases de datos de imagenes
muy grandes.
Uno de los atributos mas importantes del CEDD es la poca capacidad de compu-
tacion necesaria para su extraccion en comparacion con las necesidades de la mayorıa
de descriptores del estandar MPEG-7 [Sikora, 2001].
MPEG-7 ColorLayout o Descriptor de la Distribucion del Color
ColorLayout o Descriptor de la Distribucion del Color (DDC) es una caracterıstica
de imagen del estandar MPEG-7. El proceso de extraccion de las caracterısticas se
compone de dos partes: la rejilla o parte de la imagen basada en la seleccion del color
mas representativo y la Transformada Discreta del Coseno mediante su cuantizacion.
MPEG-7 EdgeHistogram o Histograma de Bordes
Edge Histogram [Park, Dong Kwon et al., 2000] es un descriptor basado en la tex-
tura de imagen. Es uno de los tres descriptores basados en textura del estandar de
MPEG-7. El descriptor divide la imagen en bloques mas pequenos para encontrar
los bordes a partir de la norma de lıneas
PHOG o Histograma Piramide de Gradientes Orientados
El Histograma Piramide de Gradientes Orientados [Bai, Yang et al., 2009] (PHOG
en adelante) es un descriptor para la deteccion rapida de objetos. La tecnica cuenta
las ocurrencias de orientacion de gradiente en porciones localizadas de una imagen.
Este metodo es similar al descriptor anterior, MPEG-7 EdgeHistogram, pero con-
trasta por su mayor precision a pesar de variaciones en la imagen. En el estudio
de reconocimiento de caras con PHOG realizado en el Institute of Mechanical and
Electronic Engineering4, en China, se obtienen resultados con mas del 90% de exito.
La imagen de la figura 3.6 muestra un ejemplo de la implementacion PHOG
realizada por Anna Bosch y Andrew Zisserman en University of Oxford 5.
4http://www.aicit.org/AISS/ppl/AISS1591PPL.pdf5http://www.robots.ox.ac.uk/ vgg/research/caltech/phog.html
22 Capıtulo 3
Histograma de Color RGB
Un histograma de color es la representacion de la distribucion del color en una
imagen. Es decir, representa el numero de pıxeles que tienen colores en cada una
de las listas fijas de rangos de colores, en este caso RGB (Rojo, verde y azul). La
principal ventaja es su facil extraccion, sin embargo tiene limitaciones al usarse
para clasificar imagenes con colores similares pero contenido totalmente diferente.
Por eso, no es suficiente para la busqueda de imagenes similares. Las caracterısticas
del histograma combinadas con el resto de descriptores permite encontrar mejores
resultados.
3.4.2 Indexacion y Busqueda de Imagenes en Lucene
Lire utiliza el motor de busquedas en Lucene. En primer lugar, las imagenes almace-
nadas se indexan en un ındice de Lucene para cada descriptor. Por cada descriptor
se almacenan uno o dos ındices:
• Por indexacion aproximada basada en los espacios metricos con ındices inver-
tidos, basada en los trabajos de Giuseppe Amato
[Amato, Giuseppe and Savino, Pasquale, 2008].
• Por indexacion aproximada basada en la localizacion sensible de hash de las
caracterısticas.
Las busquedas de imagenes similares en Lucene se realizan a partir de tres ma-
neras:
• Busquedas lineales, comparando los valores del vector de entrada con el de
todas las imagenes indexadas.
• Busqueda de similitud aproximada del ındice de espacios metricos con archivos
invertidos.
• Busqueda de hash representativo del vector de caracterısticas.
La indexacion de espacios metricos con ındices invertidos se utiliza para las
busqueda de imagenes con descriptores similares a los datos de la entrada. Un ındice
invertido utiliza un vocabulario (conjunto de terminos distintos del texto) y una lista
Fundamentos tecnologicos 23
de documentos donde cada termino aparece. Segun el numero de ocurrencias para
cada termino se devuelve una lista de documentos del ındice con una puntuacion de
similitud.
Cuando se desea buscar imagenes por sus caracterısticas, Lucene devuelve una
lista de documentos ordenada por la puntuacion que determina el grado de similitud
en base a ındices generados. Esta puntuacion indica el grado de similitud respecto
a la imagen data, siendo cero el valor de maxima similitud.
3.5 JHIPSTER
JHipster es un generador de codigo para proyectos web en Java con las tecnologıas
de Spring Boot y AngularJs. Su objetivo es crear una aplicacion web robusta y
moderna con las ultimas tecnologıas con el alto rendimiento de Java con Spring
Boot en el lado del servidor y un front-end elegante con AngularJs y Boostrap.
JHipster se ha convertido rapidamente en uno de los proyectos de desarrollo en
Java mas populares de GitHub [git, ]. Actualmente se encuentras en la version 2.
Se basa en la tecnologıa de Yeoman [Osmani et al., 2015], un ecosistema gene-
rador para multiples aplicaciones de scaffold o andamiaje. En el caso de JHispter,
permite crear los proyectos base mediante lınea de comandos y seleccionar los tipos
de herramientas, sistema de autenticacion y sistema de gestor de base de datos que
se desean utilizar en el proyecto.
Soporta la creacion de proyectos bases de datos relacionales y bases de datos
NoSQL como MongoDB y Cassandra. A traves de Spring Security, integra mecanis-
mos de autenticacion basada en token y OAuth2. Incluye varios idiomas por defecto
para la internacionalizacion y localizacion y ademas configura por defecto varias
librerıas de Spring como el gestor de plantillas para HTML Thymeleaf, servicio
de envıo de correo electronico, metricas y registro de logs o llamadas a metodos
asıncronos con Spring Batch.
Para la aplicacion back-end, crea el arquetipo para gestores de dependencias
Maven (opcion por defecto) y Gradle. Para el front-end se basa utiliza gestores
de aplicaciones JavaScript basadas en NPM (Node Package Manager) de Node
[Foundation, 2015] y la herramienta Bower
[Hornton, Jacobt and MacCaw, Alex, 2015] como gestor de las dependencias de li-
24 Capıtulo 3
brerıas JavaScript. Tambien utiliza Grunt [Alman, Ben and Development Team, 2015]
(opcion por defecto) o Gulp para la gestion del contenido web estatico, CSS en for-
mato SASS o LASS y compresion de las librerıas JavaScript.
Facilita ademas la generacion automatica de entidades junto con la definicion de
atributos, algunos tipos de datos y su validacion, asociacion entre entidades (solo en
bases de datos relacionales) y seleccion de paginacion en listados. JHipster generara
automaticamente una lista de operaciones CRUD (Creacion, Lectura, Actualizacion
y Eliminacion) muy basica.
3.5.1 Aplicacion Lado Servidor o Back-End
En el lado servidor o back-end se crea una aplicacion completa en Spring [Spring, 2015a].
Muchos de las librerıas son componentes de Spring y Spring Boot.
• Soporte para Java 8 con la ventaja del uso de expresiones lambda.
• Spring Boot [Spring, 2015b] para la configuracion facil del proyecto mediante
anotaciones en sustitucion del XML.
• Maven [Apache, 2015d] o Gradle para la compilacion, pruebas y arranque de
la aplicacion.
• Spring Secutiry para la autenticacion y los sistemas de seguridad.
• Spring MVC REST y Jackson para la construccion de una API RESTful.
• Spring Websocket como parte opcional para uso de WebSockets.
• Spring Data JPA y Bean Validation en el caso de uso de bases de datos rela-
cionales.
• Soporte de indexacion para busquedas con ElasticSearch (solo en bases de
datos relacionles). Esta opcion es sustituida en el trabajo de fin de master por
la solucion en Solr de DataStax Enterprise Search.
• Soporte de bases de datos NoSQL Cassandra y MongoDB.
Fundamentos tecnologicos 25
En el lado servidor se anaden por defecto dos perfiles: desarrollo y produccion,
con monitorizacion mediante metricas de Metrics, cacheo de segundo nivel opcional
con EhCache o Hazelcast, optimizacion de recursos estaticos comprimidos en GZIP,
gestion de logs ademas de la compilacion en ficheros WAR o JAR para su puesta en
marcha en un servidor de aplicaciones
Maven
Maven es una herramienta de software para la gestion y construccion de proyectos
Java. Tienen funcionalidades similares al proyecto de Apache Ant, pero con un
modelo de configuracion de construccion mas simple basado en un formato XML
(fichero pom.xml).
Permiten anadir las dependencias necesarios tables como los componentes de
Spring y otras librerıas necesarias para la ejecucion de la aplicacion web o pruebas
unitarias. El ciclo de vida de un proyecto en Maven son: compile, test, package,
install y deploy. Con Spring Boot se anaden el despliegue en spring-boot:option.
Spring Boot
Spring es un conjunto amplio de componentes que facilitan el desarrollo de aplica-
ciones Java. Fue creado por Rod Johnson en 2002 y esta escrito bajo licencia de
codigo abierto Apache License 2.0 6.
Spring Boot [Spring, 2015b] nace como subproyecto de Spring para facilitar,
mediante anotaciones y evitando el uso de ficheros XML para Spring, la creacion
rapida y agil de aplicaciones web. Spring Boot provee configuraciones por defecto
para Spring y otra gran cantidad de librerıas, ademas provee un modelo de progra-
macion parecido a las aplicaciones java tradicionales que se inician en el metodo
main. Para conseguir la puesta en marcha lo antes posible recurre a clases main y
anotaciones como @EnableAutoConfiguration o @Configure para arranque, pruebas
unitarias, compilacion o empaquetado de la aplicacion en formato WAR o JAR.
6http://www.apache.org/licenses/LICENSE-2.0.html
26 Capıtulo 3
Spring MVC
Para crear la API RESTful se utilizan el componente de Spring MVC REST [?]
y Spring Actuator. El marco (MVC) modelo-vista-controlador de Spring esta di-
senado en torno a un “DispatcherServlet” que envıa las peticiones a los manejadores
del servidor de aplicaciones con opciones configurables de asignacion al controlador,
resolucion de la vista, localizacion y zona horaria; y la resolucion de temas, ası como
soporte para la subida de archivos al servidor. El controlador predeterminado se basa
en anotaciones, ofreciendo una amplia gama de metodos de manipulacion flexibles de
datos. Desde la version de Spring 3.0, el controlador permite crear sitios Web REST-
ful y aplicaciones a traves de las anotaciones @PathVariable y @RequestMapping.
El tipo de datos devuelto es “json/application”.
Para generar de forma automatica la documentacion de la API a partir del plugin
de JavaScript Swagger UI7 y Spring Fox UI8.
MapStruct
JHipster utiliza MapStrut [Morling, 2005] para el mapeo de objetos DTO (Data
Transfer Object) que se mapean como datos de entrada/salida con las entidades.
El uso de objetos DTO en lugar de los objetos de entidades en el servicio REST es
importante para evitar conflictos en los tipos de datos de los atributos. Mediante el
mapeo con MapStruct se puede automatizar el proceso de conversion de los tipos
de datos recibidos y seleccionar solo aquellos atributos de la entidad que se desean
mostrar.
3.5.2 Aplicacion Lado Cliente o Front-End
En el lado cliente o front-end se crea una aplicacion web sencilla con las siguientes
caracterısticas y frameworks:
• Diseno web responsive.
• HTML5 Boilerplate.
7http://swagger.io/swagger-ui/8https://github.com/springfox/springfox/tree/master/springfox-swagger-ui
Fundamentos tecnologicos 27
• Twitter Boostrap.
• Framework de JavaScript AngularJs.
• Internacionalizacion con Angular Translate.
• Soporte opcional para diseno con Sass9.
Con el flujo de trabajo de desarrollo de Yeoman se anade incluye la facil ins-
talacion de dependencias con Bower, compilacion y optimizacion de Javascript con
Grunt o Gulp y pruebas unitarias con Karma10 y PhantomJs11.
Twitter Bootstrap
La interfaz o front-end es la parte visual de la aplicacion que interactua con cliente.
Twitter Bootstrap [Otto, 2015] es uno de los frameworks front-end de codigo
fuente libre bajo licencia MIT mas populares y mas descargado de en Github.
Originalmente fue creado por Mark Otto y Jacob Thornto para Twitter en 2010 y
liberado en 2011. Se ha convertido en uno de los frameworks front-end mas populares
y proyectos de codigo abierto en el mundo.
Desde la version 2.0 se incluyen funcionalidades “responsive” o diseno web adap-
table, filosofıa de diseno cuyo objetivo es adaptar la apariencia de las paginas web
al dispositivo que se este utilizando.
La palabra Bootstrap se puede traducir al castellano como “Correa de arranque“,
y es que gracias a su hoja de estilos CSS (tambien en Sass 12), temas y componentes
(a traves de jQuery) se puede desplegar en muy poco tiempo una interfaz amigable
y facil de utilizar explotando el uso de etiquetas y atributos en HTML5.
Algunas de las caracterısticas de componentes mas importantes de Bootstrap
son:
• Sistema de rejilla: incluye un sistema de rejilla fluido como respuesta para
moviles que se ajusta adecuadamente hasta 12 columnas con el tamano optimo
de ventana. Incluye clases predefinidas para opciones de diseno faciles.
9https://www.npmjs.com/package/node-sass10http://karma-runner.github.io/0.13/index.html11http://phantomjs.org/12https://github.com/twbs/bootstrap-sass
28 Capıtulo 3
• Modal ventana desplegable.
• Dropdown, Navbar y Draggable: el menu desplegable y barra de nave-
gacion facilita la creacion de menus desplegables. Ademas, incluyen la funcio-
nalidad Collapse que permite ocultar los menus en funcion del tamano de la
ventana y utilizar un boton de despliegue para desplegar los enlaces.
• Glyphicons: mas de 200 iconos de Glyphicon13.
Grunt
Grunt es un gestor de tareas para la compilacion, pruebas y ejecucion de aplicaciones
web en JavaScript. Para desarrollo, permite la modificacion en caliente del codigo y
ejecucion de pruebas test a fondo. Para produccion, comprime y optimiza todos los
recursos, soporta ademas la compilacion de hojas de estilo en codigo Sass.
Grunt se encarga de descargar y compilar las librerıas de JavaScript. Con la
opcion serve ejecuta un servicio web y lanza la aplicacion en el servidor (por defecto
en el puerto 3000 de localhost). Cada vez que se actualice un fichero, Grunt refresca
la pagina con los cambios realizados.
AngularJS
La interfaz web; font-end de la aplicacion; se autogenera a partir de JHipster. Por
defecto utiliza el framework AngularJs [Google, 2015a], el cual se comunica con el
back-end a traves de la API REST generada con la librerıa de Spring MVC Rest.
AngularJs es un framework de JavaScript de codigo abierto y adquirido por
Google en 2013 basado en programacion declarativa para la creacion de interfaces
de usuario. Promueve el uso de patrones de diseno como Single Page Application o
Delegate.
Incorpora una arquitectura MVVC (Model-View-ViewModel-Controller) con una
capa adicional conocida como “Modelo de la vista”. A diferencia de los frameworks
clasicos de JavaScript como jQuery, Angular soporta doble binding de los datos 14.
Esto significa que en vez de referenciar continuamente el arbol DOM, delega los
13http://glyphicons.com/14Two-Way Data Binding: https://docs.angularjs.org/guide/databinding
Fundamentos tecnologicos 29
datos desde el cliente hacia el controlador con la capa intermedia modelo de la vista
o ViewModel.
El modelo de la vista o ViewModel provee a Angular una gran flexibilidad a
la logica de presentacion, bien separada de la logica de negocio y el estado de su
presentacion.
3.5.3 Creacion de un proyecto en Cassandra con JHipster
A continuacion se describe como generar un proyecto base con Cassandra usando la
herramienta de JHipster desde lınea de comandos.
$ npm install -g yo bower grunt -cli generator -jhipster
$ mkdir cassandra -proyect && cd cassandra -proyect
$ yo jhipster
? (1/15) What is the base name of your application? cassandra_sample
? (2/15) What is your default Java package name? com.example
? (3/15) Do you want to use Java 8? Yes (use Java 8)
? (4/15) Which *type* of authentication would you like to use? Token -based
authentication (stateless , with a token)
? (5/15) Which *type* of database would you like to use? Cassandra
? (10/15) Do you want to use clustered HTTP sessions? No
? (11/15) Do you want to use WebSockets? No
? (12/15) Would you like to use Maven or Gradle for building the backend?
Maven (recommended)
? (13/15) Would you like to use Grunt or Gulp.js for building the frontend?
Grunt (recommended)
? (14/15) Would you like to use the Compass CSS Authoring Framework? No
? (15/15) Would you like to enable translation support with Angular
Translate? Yes
3.5.4 Problemas y Bugs encontrados
A pesar de la rapida popularidad de JHipster, es una aplicacion reciente (menos de
dos anos) que aun incluye errores, bugs y mejoras por realizar en su version estable.
Uno de los mayores problemas de la version estable con soporte para Cassandra es
el bug generado por la dependencia de Google Guava 14.0.1 15 que no permite su
despliegue en servidores Java EE.
Para resolver el problema, en el fichero pom.xml Maven, anadir las dependencias
y actualizar la version de Cassandra y Guava.
15Bug Guava 14.0.1 https://code.google.com/p/guava-libraries/issues/detail?id=1433
30 Capıtulo 3
1 <datastax -driver.version >2.1.7.1 </datastax -driver.version >
2 <datastax -driver.version >2.1.4 </datastax -driver.version >
3 <springfox.version >2.1.2 </springfox.version >
Eliminar la dependencia de Cassandra-all.
1 <dependency >
2 <groupId >org.apache.cassandra </groupId >
3 <artifactId >cassandra -all</artifactId >
4 <version >${ cassandra.version}</version >
5 <exclusions >
6 <exclusion >
7 <artifactId >slf4j -log4j12 </artifactId >
8 <groupId >org.slf4j </groupId >
9 </exclusion >
10 </exclusions >
11 </dependency >
Anadir la dependencias del driver de Cassandra DataStax:
1 <dependency >
2 <groupId >org.springframework.data</groupId >
3 <artifactId >spring -data -cassandra </artifactId >
4 </dependency >
5 <dependency >
6 <groupId >com.datastax.cassandra </groupId >
7 <artifactId >cassandra -driver -core</artifactId >
8 <version >${datastax -driver.version}</version >
9 <exclusions >
10 <exclusion >
11 <groupId >com.codahale.metrics </groupId >
12 <artifactId >metrics -core</artifactId >
13 </exclusion >
14 </exclusions >
15 </dependency >
16 <dependency >
17 <groupId >com.datastax.cassandra </groupId >
18 <artifactId >cassandra -driver -mapping </artifactId >
19 <version >${datastax -driver.version}</version >
20 </dependency >
3.5.5 Instalacion de Cassandra y Ejecucion en desarrollo
Para ejecutar la aplicacion generada con Maven se utiliza la opcion de arranque
de Spring Boot. Para la instalacion de Cassandra se pueden usar los paquetes de
instalacion de DataStax Community Edition [DataStax, 2015b]:
$ curl -L http :// debian.datastax.com/debian/repo_key | sudo apt -key add -
$ sudo apt -get update
$ sudo apt -get install dsc21 =2.1.x-1 cassandra =2.1.x
$ sudo apt -get install cassandra -tools =2.1.x ## Optional utilities
A continuacion, crear el KeySpace y las tablas de Cassandra con la herramienta
cqlsh:
Fundamentos tecnologicos 31
$ cqlsh -f src/main/resources/config/cql/create -keyspace.cql
$ cqlsh -k cassandra -proyect -f src/main/resources/config/cql/
create -tables.cql
$ cqlsh -k cassandra -proyect
cqlsh:cassandra -proyect > DESC cassandra -proyect;
...
cqlsh:cassandra -proyect > exit;
En modo produccion se deberan crear las tablas del fichero create-keyspace-
prod.cql.
Para ejecutar la aplicacion con Maven.
$ mvn compile
$ mvn spring -boot:run
Un vez compilado y ejecutado el servidor back-end (puerto 8080 por defecto), se
recomienda ejecutar la aplicacion de Grunt que genera y actualizar cualquier cambio
del front-end.
$ grunt test
$ grunt serve
La aplicacion de abrira por defecto en el navegador en la direccion ruta de
http://localhost:3000.
32 Capıtulo 3
Figura 3.3: Pruebas de Rendimiento de Cassandra por Netflix
Fundamentos tecnologicos 33
Figura 3.4: Funcionalidades del producto DataStax Enterprise
Figura 3.5: Logo del proyecto Lire.
34 Capıtulo 3
Figura 3.6: Ejemplo de descriptor de caracterısticas PHOG.
Figura 3.7: JHipster crea proyectos web con Spring Boot y AngularJs.
Fundamentos tecnologicos 35
Figura 3.8: Spring Boot automatiza la construccion y configuracion de proyectos en
Spring.
Figura 3.9: MMVC en AngularJs.
36 Capıtulo 3
Figura 3.10: Enlace doble de los datos en Angular.
4METODOLOGIA DE DESARROLLO
4.1 INTRODUCCION
Para el desarrollo de la aplicacion se ha seguido una planificacion basada en la
metodologıa de desarrollo agil Scrum1, un metodo para el desarrollo de software
basado en procesos iterativos e incrementales.
El motivo de realizar la aplicacion basandose en esta metodologıa es que permite
una gestion regular de las expectativas de entrega para cada etapa. Estas, al ser
entregadas con funcionalidades completas, permite que se pueda empezar a utilizar
los resultados mas importantes del proyecto antes de que este finalizado por completo
y gestionar el proyecto de una forma mas agil. En la figura 4.1 se muestra el diagrama
de los procesos de un proyecto realizado en Scrum.
1http://www.scrumalliance.org/pages/what is scrum
37
38 Capıtulo 4
Figura 4.1: Marco de Trabajo Scrum.
4.2 SCRUM
La palabra Scrum procede del vocabulario del rugby y significa mele, es decir, esa
figura en la que los companeros del equipo se amontonan, forman una pina y empujan
todos en la misma direccion.
El metodo es un modelo de referencia que define un conjunto de practicas y roles
que pueden tomarse como punto de partida para definir el proceso de desarrollo que
se ejecutara durante el proyecto.
Roles
Los roles principales en Scrum son el Scrum Master, que mantiene los procesos y
trabaja de forma similar al director del proyecto; el Product Owner o propietario del
proyecto, dueno del producto quien realiza peticiones y que representa al cliente; y
el Scrum Team que incluye al equipo de desarrollo y realiza el trabajo de analisis,
diseno, implementacion, pruebas y documentacion.
Tambien existen roles auxiliares como los interesados, quienes asesoran y obser-
van el proceso de desarrollo.
En el caso del proyecto el director realiza el papel de Product Owner y Scrum
Metodologıa de desarrollo 39
Master mientras que el alumno del Scrum Team.
En el siguiente diagrama 4.2 se muestran los distintos roles de la metodologıa y
los procesos en los que estan implicados.
Figura 4.2: Proceso de desarrollo con Scrum.
Sprint
El Sprint es el perıodo en el cual se lleva a cabo el trabajo en sı. Es recomendado
que la duracion de los sprints sea constante y definida por el alumno en base a su
propia experiencia.
Con cada sprint se realiza una serie de caracterısticas funcionales en un margen
de 15 a 30 dıas.
Los sprint propuestos comienzan por anadir tareas basicas como registro de usua-
rios, inicio de sesion, creacion de proyectos. En las siguientes etapas se han ido
anadiendo funcionalidades agregadas a las primeras. Cada sprint es completado con
40 Capıtulo 4
acciones determinadas para que la aplicacion tenga areas completamente funcionales
desde el momento en que se finaliza.
Product backlog
El product backlog es un documento que contiene descripciones genericas de todos
los requerimientos, funcionalidades deseables y prioridades. Contiene estimaciones a
grosso modo, del esfuerzo de desarrollo requerido. Esta estimacion ayuda al product
owner a ajustar la lınea temporal y, de manera limitada, la prioridad de las diferentes
tareas.
Sprint backlog
Con el sprint backlog se crea un documento detallado donde se describe como se
van a implementar los requisitos durante el siguiente sprint. Las tareas se dividen
en horas con ninguna tarea de duracion superior a 16 horas. Si una tarea es mayor
de 16 horas, debera ser divida en otras menores.
Las etapas de la aplicacion se realizan en ciclos de desarrollo lo mas cortos
posibles. Para lograrlo se utiliza el sprint de requisitos o sprint backlog, una lista en
la que se detalla como se van a construir los diferentes requisitos de la aplicacion.
Los requisitos del product backlog se dividen para transformarlos en tareas de menos
de un dıa de desarrollo.
Cada “sprint” suele realizarse en un plazo de entre 2 y 4 semanas. Al final,
el objetivo es entregar algo que funcione, para que el usuario pueda probarlo y se
puedan introducir los cambios necesarios.
Reuniones
Una de la figuras fundamentales de la metodologıa Scrum es la reunion diaria de
todo un equipo. Esta debe durar alrededor de 15 minutos y se realiza de pie. En el
caso del proyecto las reuniones diarias has sido excluıdas, pero durante cada dıa del
desarrollo se realizaron las siguientes 3 preguntas clave:
• ¿Que se ha hecho desde ayer?
• ¿Que se tiene planeado hacer manana?
Metodologıa de desarrollo 41
• ¿Se ha encontrado algun problema para conseguir los objetivos?
Por otro lado esta la reunion de planificacion que se realiza al inicio del ciclo
Sprint, cada 15 o 30 dıas. El objetivo de esta reunion es seleccionar que trabajo se
hara y preparar el Sprint Backlog que detalla el tiempo que tomara hacer el trabajo.
La reunion de revision se realiza al terminar cada Sprint para comprobar que se
ha completado. En ella se presenta el trabajo completado.
Despues de cada Sprint, se lleva a cabo una retrospectiva en la cual se comentan
las impresiones sobre el Sprint recien superado. El proposito de la retrospectiva es
realizar una mejora continua del proceso.
4.3 ADAPTACION AL PROYECTO
Teniendo en cuenta las caracterısticas del proyecto, la metodologıa Scrum no se ha
aplicado directamente al proyecto, sino que se ha adaptado a las caracterısticas de
este.
Por ejemplo, la metodologıa Scrummarca reuniones diarias del equipo de trabajo.
Teniendo en cuenta el tamano del proyecto, estas reuniones no se han realizado de
forma diaria, sino semanal.
El rol de ScrumTeam es asumido por el alumno, mientras que las funciones de los
roles ScrumMaster y ProductOwner se realizaron de forma conjunta con el director
del proyecto (decidir las caracterısticas del sistema a desarrollar y la gestion de la
planificacion del proyecto).
4.3.1 Analisis
Para el analisis se ha descrito una lista de tareas importantes o product backlog tales
como el almacenamiento de imagenes, pruebas de indexacion, estudio de viabili-
dad, escalado de imagenes, extraccion de los metadatos y busquedas avanzadas por
caracterısticas de imagen.
A continuacion, una vez estimadas las tareas a realizar y las divisiones en la lista
de Sprint backlog se ha creado el modelo de datos para Cassandra.
El modelo de datos ha sido incrementado durante el proceso de desarrollo de la
aplicacion a medida que se realizaban las distintas etapas.
42 Capıtulo 4
Se han disenado los diferentes casos de uso en UML y el diagrama del modelo
entidad relacion extendido. Una vez descritos los puntos necesarios se comenzo con
la parte de diseno de la aplicacion.
4.3.2 Diseno
Se han diseno los diagramas de clases y el modelo de datos en UML junto con las
principales clases de la capa modelo con los Repository y Servicios, el controlador y
demas elementos de la aplicacion utilizando Magic Draw.
En la seccion 7.3 se muestran varios diagramas de clases de UML del diseno de
la aplicacion. En la figura 7.5 se muestra un diagrama de clases con las entidades de
la aplicacion y sus atributos.
En la figura 7.7 se describen las interfaces de los servicios de la capa modelo y el
apartado 7.3.1 describe los diagramas de las interfaces de “Accessor” y “Repository”.
Las herramientas utilizadas para el diseno estan descritas en la seccion 8.3 del
capıtulo de implementacion.
5PLANIFICACION Y SEGUIMIENTO
5.1 TAREAS Y RECURSOS
En esta seccion se describen las tareas necesarias para realizar el proyecto y de los
recursos disponibles para hacerlo (humanos y tecnicos).
Para el calculo de los costes totales se suelen tener en cuenta los recursos humanos
y tecnicos (hardware y software adicional). En este proyecto solamente se tendra en
cuenta los recursos humanos que estaran compuestos por:
• Programador, encargado de implementar el proyecto.
• Analista, responsable del analisis y diseno previa para su implementacion.
• Jefe de Proyecto, quien se ocupa de planificar las tareas y de establecer puntos
de control.
43
44 Capıtulo 5
Los roles de programador y analista han sido desarrollados por el alumno, mien-
tras que del jefe de proyecto se ha encargado el director.
Para gestionar la planificacion se ha utilizado la herramienta Project Libre. Tal
como se describe en el capıtulo de Implementacion y pruebas, en la seccion de
Herramientas utilizadas 8.3.5, se trata de un gestor de proyectos multiplataforma
escrito en Java que permite definir hitos del proyecto y visualizar diagramas de
Gantt.
Se divide el proyecto en Sprints de Scrum, con fechas de inicio, finalizacion y
horas dedicadas para cada una. De esta manera, se crearon las tareas de analisis y
diseno inicial de la aplicacion junto con 7 iteraciones para el desarrollo.
Especificacion inicial del sistema
Es esta seccion se realiza el analisis que establece los requisitos de la aplicacion,
Product backlog en Scrum, donde se define de que elementos contiene la aplicacion
y como deben ser implementados.
Tambien se decide que plataforma, arquitectura, lenguaje, frameworks y dife-
rentes herramientas se han de utilizar para el desarrollo y la implementacion de la
aplicacion.
Planificacion
Se realiza la planificacion de las tareas a realizar. Se escoge la metodologıa Scrum
se realiza la division de trabajo en varias iteraciones “Sprints”. Cada uno de ellos
es una etapa funcional completa, incluyendo la capa modelo, API e interfaz web.
Dentro de cada iteracion se realizan tareas basicas de no mas de 16 horas cada una.
Al finalizar cada iteracion se entrega la parte funcional de la aplicacion y se revisa.
Iteracion 1: Estudio de Requisitos y Tecnologıa
Parte del inicio del desarrollo de la aplicacion. En esta iteracion se realizan prue-
bas con distintas librerıas de recuperacion de imagenes como JFeatureLib 1, Lire
y el plugin de Lire para Solr [Lux, Mathias and Chatzichristofis, Savvas A., 2015b].
1https://github.com/locked-fg/JFeatureLib
Planificacion y seguimiento 45
Tambien se realiza un analisis de rendimiento de bases las de datos NoSQL y uso de
cluster para Hadoop con el objetivo de buscar la escalabilidad y tolerancia a fallos
de la aplicacion. Se estudian las posibilidades que ofrece Cassandra los paquetes de
DataStax a partir de la version libre Community Edition [DataStax, 2005].
Finalmente se hace un estudio de frameworks y lenguajes de desarrollo agiles. Se
pruebas aplicaciones como Spring Roo, Spring Boot [Spring, 2015b], Grails y Scala
con Play Framework [TypeSafe, 2005]. Tambien se estudia el proyecto de JHipster
[Dubois and Mirc, 2015] con su soporte temprano para la creacion de proyectos y
entidades con Cassandra.
Algunas de las pruebas realizadas con estas tecnologıas se encuentran en el re-
positorio del alumno en GitHub [Albela Perez, 2005].
Iteracion 2: Creacion de la Aplicacion Base
En esta iteracion se crea la estructura del proyecto de inicio con el generador JHipster
para Java 8 y base de datos Cassandra. La autenticacion utilizada para el acceso a
la API esta basada en token de sesion sin estado. Se instala la version de Cassandra
2.1 DataStax Communit Edition y se solucionan una serie de bugs de JHipster con
Cassandra y las dependencias de Maven descritos en el capıtulo de Implementacion
en la seccion 3.5.4. A partir de este proyecto se empieza a configura todo el sistema,
se realizan las primeras pruebas y se integra con Lire para utilizar los ındices de
Lucene.
Iteracion 3: Almacenamiento de Imagenes
En la iteracion 3 se ha realizado ha creado la entidad de imagenes con la opcion de
almacenamiento de las mismas. En primer lugar se crea el modelo de datos adaptado
a Cassandra y se definen los atributos. Las imagenes son almacenadas como datos de
tipo BLOB en la propia base de datos, y son reescalada en varios tamanos: pequeno,
mediano y grande. Ademas, se realiza una desnormalizacion del modelo basandose
en los consejos de artıculos del blog de Ebay [Ebay, 2012].
Se desarrolla ademas el servicio REST recibe las imagenes mediante metodos
POST, uno a traves de objetos DTO de tipo byte[] y otro metodo a traves de
la cabecera del controlador de Spring MVC con parametros de tipo MultpartFile.
46 Capıtulo 5
Tambien se crean los servicios web para el acceso de imagenes de un usuario concreto
(con edicion y borrado) y el servicio restringido solo para usuarios administradores.
En esta iteracion tambien se disena una interfaz web basica para el envıo de
ficheros a traves de AngularJs. Para ello, se reescriben el controlado y servicios
JavaScript de la interfaz.
Iteracion 4: Extraccion de los Metadatos
En esta iteracion se realiza la extraccion de la informacion contenida en las cabeceras
de las imagenes. Esta es extraida y almacenada en Cassandra en una tabla dinamica.
Los datos son incluido como tipo texto. Tambien se desarrollan las clases de pruebas,
la API de acceso a los datos y para el panel de administracion (edicion y borrado
de cualquier metadato). Se crea un interfaz web basica con acceso a los metadatos,
visualizacion y el panel de administracion.
Iteracion 5: Extraccion e Indexacion de Descriptores de Ima-
gen
En el quinto “Sprint” se anade a la capa de servicios los metodos de extraccion
e indexacion de los vectores de los descriptores de imagenes utilizados con Lire.
Los vectores de los distintos descriptores se anaden como atributos a la entidad de
imagenes y se guardan en como vectores y de forma serializada en binario. Durante
las pruebas de test se anaden pruebas de extraccion con distintos tipos de imagenes.
En esta iteracion se descartan algunos descriptores no optimos para su uso en el
servidor de aplicaciones por problemas de rendimiento.
Iteracion 6: Integracion de Solr y Busquedas
Se anade la base de datos de Cassandra con DataStax Enterprise Search [DataStax, 2015c]
y su integracion para el motor de busquedas Solr [Cutting, 2015a]. Las tablas de
Cassandra son reindexadas en Solr y se anaden las operaciones de busqueda para
las imagenes y los metadatos en la API, los servicios y los Repository. La interfaz
tambien es adaptada para las busquedas anadiendo los formularios de busqueda.
Planificacion y seguimiento 47
Iteracion 7: Recuperacion Avanzada de Imagenes
En esta ultima iteracion se crean las entidades para almacenar y realizar las busque-
das por imagenes. A partir de los ındices creados en la iteracion 55.1. Para ello es
necesario crear 2 entidades: para los datos de la busqueda y para los datos de los
resultados obtenidos. Las busquedas se almacenan junto con sus caracterısticas de
descriptores extraıdas. Los resultados de la busqueda, obtenidos gracias a Lire y los
ındices de Lucene, son almacenados junto con la puntuacion obtenida.
El servicio REST incluye dos operaciones, una para enviar busqueda directamen-
te como fichero y otra como DTO (necesarios para la interfaz con AngularJs). La
interfaz web realiza la operacion de busqueda enviando la imagen del usuario, ob-
tiene el identificador del DTO creado y se redirige a la lista de resultados ordenada
por la puntuacion.
5.2 PLANIFICACION INICIAL Y COSTE
Durante los meses de enero a mayo se han realizado tareas de estudio de analisis y
viabilidad, en estas tareas se incluye el estudio de tecnologıas apropiadas para el uso
de la aplicacion como las bases de datos NoSQL Cassandra, MongoDB y HBase.
Las fases de toma de requisitos, analisis y diseno junto con la asignacion de
“Sprints” de Scrum, el diseno y la implementacion para la aplicacion final comienzan
a mediados de mayo.
Inicialmente, se estima una duracion total de la aplicacion en 55 dıas laborales
(del 18 de mayo al 1 de agosto). Sin embargo, por retrasos en la implementacion,
fases de aprendizaje de las tecnologıas, el desarrollo completo de la aplicacion se ha
visto retrasada hasta finales de Agosto con un total de 75 dıas laborales.
El precio establecido de coste por mano de obra es de 40 ela hora, realizando 40
horas semanales. El coste estimado para las 440 horas (55 dıas) fue de 17600 e. Sin
embargo por los retrasos indicados, asciende a un total de 600 horas con un coste
final de 24000 epor mano de obra mas 400 epara material informatico.
En la figura 5.1 se muestra el diagrama de Gantt con las fechas de las iteraciones
“Sprint”.
48 Capıtulo 5
Figura
5.1:
Diagram
adeGan
ttconplanificacion
delasiteraciones.
Planificacion y seguimiento 49
5.3 SEGUIMIENTO
El objetivo del seguimiento final es comparar la prevision inicial y contrastarla con
la real para aprender de la experiencia, buscando las desviaciones e interpretando el
suceso que las origino.
Se ha producido una serie de sucesos que han variado la estimacion en cada una
de las iteraciones realizadas, causadas principalmente por:
• Conocimiento amplio de las tecnologıas como Cassandra, DataStax y JHipster.
• La instalacion y configuracion de Cassandra DataStax y DSE Search.
• Correccion de bugs de JHipster en la version de Cassandra.
• Cambios en los requisitos y variacion en el diseno para que algunos modulos
funcionasen de forma correcta. Por ejemplo, en el caso de la busqueda por
imagen se anadieron nuevas extraccion de imagenes, se integro la indexacion
de los datos de Cassandra en Apache Solr.
• La creacion de un front-end valido como prueba de concepto, cambio en los
controladores de AngularJs.
• Correccion de fallos importantes en la generacion de codigo del proyecto de
JHipster con soporte para Cassandra3.5.4.
• Refactorizacion de codigo, inclusion de las interfaces Accessor de acceso a Cas-
sandra, aprovechamiento de las operaciones lambda de Java 8, modificacion del
modelo inicial de datos de Cassandra (uso de ındices compuestos, ordenacion
por columna cluster).
50 Capıtulo 5
6ANALISIS
Este capıtulo presenta el resultado del analisis de requisitos realizado durante el
proyecto. Esta seccion describe el listado de casos de uso del sistema desarrollado,
agrupados en modulos, y los requisitos no funcionales.
Se describen los principales casos de uso realizados en la aplicacion. Un diagrama
de casos de uso contiene elementos de modelo para el sistema, los actores y sus casos
de uso y muestra las diferentes relaciones tales como generalizacion, asociacion y
dependencia entre estos elementos.
6.1 USUARIOS
El modulo de usuarios incluye todas las funcionalidades para el usuario del sistema
tales como alta o registro de usuario, inicio de sesion, actualizacion de los datos,
cambio de contrasena y vistas del perfil de usuario.
En la figura 6.1 se muestran los casos de uso de todos los usuarios del sistema.
51
52 Capıtulo 6
Figura 6.1: Caso de uso de usuarios
Cada uno de los casos de uso de usuario son descritos a continuacion.
• Sign up: alta de nuevo usuario. Este caso de uso implica la generacion de un
token de activacion.
• Generate activation token: creacion del token de verificacion del nuevo usuario,
, con su fecha de creacion. Implica enviar un email al usuario con un enlace
para la activacion.
• Send email. envıo de un correo electronico para el usuario. Utilizado en el envıo
de token de activacion o recuperacion de contrasena.
• Activate user : caso de uso de activacion del usuario a traves de un token
generado. Este token se comprueba que pertenezca al usuario y que no hay
caducado.
• Sign In: iniciar sesion el usuario en la aplicacion. Una vez se inicie la sesion,
el usuario podra acceder a su recursos del sistema, imagenes y metadatos.
• Update profile: actualizar perfil del usuario. El nombre de usuario y el email
no se pueden modificar.
Analisis 53
• Change password : cambiar la contrasena del usuario. La contrasena se debe
indicar dos veces y debe tener entre 5 y 50 caracteres.
• Sign Out : cerrar sesion de usuario. Para salir de la aplicacion.
• Recover password : si el usuario olvida su contrasena, se anade un caso de uso
de recuperar contrasena a traves del nombre de usuario o email. Esta caso de
uso implica la generacion de un token para recuperar la contrasena con una
fecha de validez temporal.
• Generate recover password token: crear un token de recuperacion de contrasena
olvidada por el usuario.
6.2 IMAGENES
Las imagenes almacenadas en la plataforma contienen informacion relevante como
la propia imagen en diferentes tamanos, la imagen original inalterada, un tıtulo,
descripcion, fecha de publicacion, fecha de modificacion y datos sobre el usuario.
Cuando una imagen es almacenada, se escala su resolucion en diferentes tamanos
y se guardan para mostrar segun el tipo de interfaz (listados, galerıa, vista). Ademas
se extraen varios de los vectores de descriptores de caracterısticas que representan
a una imagen.
En la figura 6.2 se muestran los casos de uso de imagenes.
Los casos de uso para las imagenes son:
• Upload picture: subir una imagen. Las imagenes deben contener al menos un
tıtulo. Los formatos permitidos son png, jpg, tiff y gif. No pueden superar los
10 megas de tamano de imagen. El proceso de subida de una imagen implica
varios casos de uso que se deben realizar de forma asıncrona: escalar imagen,
extraer metadatos y extraer e indexar caracterısticas de la imagen.
• Scale picture: escalar una imagen en tres tamanos: pequeno, mediano y grande.
• Extract metadata: extraer los metadatos de una imagen para almacenarlos en
la base de datos, con todos sus datos: directorio del metadato, nombre y tipo
de la etiqueta y descripcion.
54 Capıtulo 6
Figura 6.2: Caso de uso de imagenes
• Extract & Index Features : extraccion e indexacion de las diferentes caracterısti-
cas para su posterior busqueda.
• List Pictures : listar imagenes almacenadas, ordenadas por fecha de creacion
en modo descendente.
• Search pictures by text : buscar imagenes en la base de datos a partir de con-
tenidos de texto u otros campos no binarios.
• View Picture: ver los datos de una imagen.
6.3 IMAGENES DE USUARIO
Cada usuario contiene un album con sus propias imagenes almacenadas. Estas solo
pueden ser editadas o eliminadas por el propio usuario desde. La API permite per-
mite el acceso directo si el usuario esta autenticado en la aplicacion. La interfaz web
permite acceder a las imagenes del usuario desde un enlace de la cabecera.
En la figura 6.3 se muestran los casos de uso de imagenes.
Los casos de uso para las imagenes de un usuario son:
• List My Pictures : listar las imagenes almacenadas por el usuario de sesion.
Analisis 55
Figura 6.3: Caso de uso de mis imagenes
• View My Picture: ver un imagen que pertenezca al usuario de sesion.
• Edit My Picture: editar la imagen que pertenezca al usuario de sesion. Si otro
usuario intenta editar un recurso que no le pertenece, la aplicacion debe devol-
ver un error de recurso no encontrado. Este caso de uso implica actualizacion
de los metadatos.
• Update Metadata: actualizar el tıtulo de una imagen en todos los metadatos
de la imagen asociada.
• Delete My Picture: eliminar una imagen. Si un usuario intenta eliminar un
recurso que no le pertenece, la aplicacion debe devolver un error de recurso
no encontrado. Este caso de uso implica eliminar metadatos de una imagen
asociada y los ındices de caracterısticas almacenados para busquedas.
• Delete Metadata: eliminar todos los metadatos de una imagen eliminada.
• Delete Index Features : eliminar los ındices de caracterısticas de una imagen.
56 Capıtulo 6
6.4 METADATOS
Para cada imagen almacenada se extrae su meta-informacion, de diferentes tipos, y
se guarda en la tabla de los metadatos para ser mostrada y poder obtener una lista
de todos los metadatos. Esta informacion puede indicar la posicion geografica, el
formato de colores o en caso de fotografıa digital, la marca y modelo de la camara,
los parametros de exposicion o las fechas de exposicion. Con estos datos guardados,
es posible realizar busquedas de imagenes a partir de sus metadatos.
En la figura 6.4 se muestran los casos de uso de metadatos.
Figura 6.4: Caso de uso de mis imagenes
Los casos de uso para los metadatos de una imagen son:
• Metadata List : listar los metadatos extraıdos de las imagenes.
• Search Metadatas by text : buscar metadatos por texto.
• Metadatas List from Picture: mostrar todos los metadatos de una imagen.
Desde este listado se pueden buscar metadatos.
• View Metadata: ver en detalle la fila de un metadato.
6.5 BUSQUEDA AVANZADA DE IMAGENES
La aplicacion permite la busqueda por cualquier campo almacenado en la base de
datos. Pero ademas, es posible realizar una busqueda a traves de los vectores de
Analisis 57
caracterısticas extraıdas de las imagenes.
En el caso de la busqueda por imagenes, se almacena la imagen que se va a
comparar con el catalogo de imagenes. A continuacion se extraen sus caracterısticas
de cada uno de los seis algoritmos y se compara cada uno con el ındice almacenado.
Las imagenes resultantes tambien se almacenan en una tabla y se muestran por
orden de similitud total o media entre todas las caracterısticas.
En la figura 6.5 se muestran los casos de uso de busqueda de imagenes.
Figura 6.5: Caso de uso busqueda avanzada de imagenes
Los casos de uso para las busquedas avanzadas de imagenes son:
• Search Picture: buscar imagen. Realizar la recuperacion de imagenes basandose
en contenido. Este caso de uso implica el almacenamiento de los datos de
busqueda. Ademas, implica la busqueda de los ındices almacenados de todas
las caracterısticas de la base de datos y el almacenamiento de los resultdos.
• Store Searched Picture: almacenar la imagen de busqueda con sus vectores
de caracterısticas extraidos incluidos y la fecha de creacion de la busqueda.
Tambien se almacena el usuario.
• Extract & Store Features : extraccion de los vectores de las caracterısticas de
la imagen buscada.
• Search & Store Retrieved Pictures : busqueda sobre los vectores indexados a
partir de la imagen de entrada y almacenamiento de los todos los resultados
con las puntuaciones de los descriptores y una puntuacion total.
58 Capıtulo 6
• List Searched Pictures : listar busquedas de imagenes realizadas.
• List Retrieved Pictures : listar las imagenes encontradas para una busqueda
concreta.
6.6 ADMINISTRACION DE IMAGENES
El administrador tiene como objetivo principal la gestion de los usuarios y el resto
de contenidos de la aplicacion. Ademas se gestiona el control de accesos a los dife-
rentes acciones de la aplicacion, visualizacion de metricas de uso de la plataforma,
logs de registro de la aplicacion y acceso a la API RESTful completamente. Es de-
cir, el usuario administrador puede editar cualquier imagen, ındices de busqueda y
usuarios.
El usuario administrador tiene acceso a todas las imagenes, metadatos y busque-
das realizadas. Podra actualizar o eliminar cualquier informacion que estime nece-
sario. Ası como realizar nuevas busquedas para comprobar los resultados.
En la figura 6.6 se muestran los casos de uso de administracion de imagenes.
Los casos de uso para las operaciones sobre recursos por un usuario con privilegios
de administracion son:
• List Pictures : listar todas las imagenes.
• Edit Picture: editar imagen.
• Delete Picture: eliminar imagen. Esto implima eliminar metadatos e ındices
de caracterısticas asociados a la imagen.
• Delete Index Features : eliminar ındice de caracterıstica de imagen.
• Delete Metadata: eliminar metadato de una imagen.
• List Metadatas : listar todos los metadatos.
• Edit Metadata: editar un metadato.
• Delete Metadata: eliminar un metadato.
• Delete Searched Picture: eliminar imagenes enviadas en las busquedas.
Analisis 59
Figura 6.6: Caso de uso de administracion de imagenes
• Delete Retrieved Pictures : eliminar imagenes de los resultados de busqueda.
60 Capıtulo 6
7DISENO
Este capıtulo presenta los principales elementos del diseno del sistema. La seccion 7.1
describe la arquitectura de la aplicacion y los principales elementos que la componen.
La seccion 7.2 presenta el modelo logico de datos de la aplicacion y, finalmente,
la seccion 7.3 detalla el diseno de algunos componentes de la aplicacion.
7.1 ARQUITECTURA DEL SISTEMA
La arquitectura de la aplicacion esta desarrollada bajo una base de datos NoSQL
Apache Cassandra, dando lugar a una facil escalabilidad y rendimiento. La aplicacion
esta configurada para dos tipos de despliegue: desarrollo y produccion.
Para las busquedas de metadatos y textos en la aplicacion se utiliza el mo-
tor de busquedas Solr, integrado con Cassandra a traves de DataStax Enterprise
Search [DataStax, 2015c]. Las busquedas avanzadas de imagenes utiliza la librerıa Li-
re [Lux, Mathias and Chatzichristofis, Savvas A., 2015a], directamente a traves del
61
62 Capıtulo 7
motor Lucene.
En modo desarrollo se utiliza un unico nodo en modo pseudo-distribuido. En
produccion la aplicacion se puede desplegar en tantos nodos de Cassandra como se
desee sin necesidad de cambiar el servidor. Tan solo se deben definir correctamente
los parametros de creacion del KeySpace de Cassandra, con un factor de replicacion
3 por defecto. Solr puede estar desplegado en uno o varios nodos tambien y es
recomendable desplegar el nodo de Cassadra y Solr en instancias diferentes para
mejorar el rendimiento en las busquedas. Para que la aplicacion se comunique con
el cluster, todos los nodos deben estar configurados con el mismo nombre a traves
de la configuracion de DataStax en el fichero “/etc/dse/cassandra/cassandra.yaml”,
con la variable “cluster name”.
El almacenamiento de las imagenes se realiza en las tablas de Cassandra, en
varios atributos de tipo “BLOB”. Los vectores de las caracterısticas extraıdas de las
imagenes (descriptores MPEG-7, CEDD, PHOG, etc) se almacenan en Cassandra
como tipo binario y como listas de tipo “Double”, pero ademas son indexadas en
el motor de Lucene con la librerıa Lire en el sistema de ficheros del servidor. Para
mejorar el rendimiento a la hora de indexar los vectores, se recomienda utilizar un
servidor con al menos 8 nucleos de procesador para la indexacion de forma paralela
realizada por Lire.
Por ultimo, la aplicacion provee una API o servicio web bajo una arquitectura
RESTful [Richardson, 2015] en el lado servidor o back-end creada con Spring Boot
[Spring, 2015b]. Mediante el componente de “Spring Security” y un sistema de au-
tenticacion basada en “token” de sesion sin estado se asegura el acceso a la API. Una
aplicacion de lado cliente o front-end realiza las peticiones HTTP mediante el uso
de la librerıa AngularJs. Los usuarios realizan peticiones directamente bien desde la
API o a traves de la interfaz web del lado cliente para obtener los datos, almacenar
o realizar busquedas por contenido de imagen.
En la figura 7.1 se muestra la arquitectura global de la aplicacion con todos sus
componentes en un despliegue. En la parte de la derecha se encuentran las instancias
o nodos para el almacenamiento de los datos en Cassandra y el motor de busquedas
de Solr (DataStax Enterprise Search). Los ındices de Lucene se encuentran en el
sistema de ficheros del servidor de la aplicacion. A continuacion, en la parte central
se muestran los componentes de Spring y Spring Boot utilizados con las entidades
Diseno 63
del modelo de datos, las llamadas a la base de datas traves de las clases “Repository”
y “Accesor”, los servicios y la liberıa de Lire. En la capa superior de Spring esta
la API REST que mapea los objetos DTO con los dominios del modelo de datos
gracias a la librerıa de MapStruct.
Por ultimo, el lado cliente escrito en AngularJS (parte izquierda) sirve para
mostrar una interfaz web a los usuarios y se comunica con la API REST. Los usuarios
de la aplicacion puede comunicarse directamente a traves de esta interfaz, mediante
una aplicacion externa (aplicacion para smarthpone) o directamente contra la API
mediante sus credenciales de usuario y un “token” de sesion.
64 Capıtulo 7
Figura
7.1:
Arquitectura
Global
dela
aplicacion
.
Diseno 65
A continuacion se describen las distintas partes de la arquitectura.
7.1.1 Cassandra DataStax Enterprise
DataStax Enterprise ofrece Apache Cassandra bajo una plataforma que reune las
demandas de rendimiento y disponibilidad de para una aplicacion Web escalable. Se
puede escalar en un solo centro de datos o a traves de multiples centros de datos y
almacenamiento en la nube.
En la figura 7.2 se muestra la segregacion de los diferentes nodos de DataStax:
Nodos de Cassandra como aplicacion de tiempo real; nodos DSE Search, para la
indexacion y busquedas en Solr; y finalmente DSE Analytics, para el analisis de los
datos en un cluster de Hadoop. Como se explica en el apartado 7.1.1, los nodos de
DSE Hadoop no son desplegados en la arquitectura de la aplicacion.
Figura 7.2: Arquitectura de segregacion de nodos en DataStax.
DSE Search
El nodo de DSE Search incorpora Solr [Cutting, 2015a] como indexador y buscador
web. DataStax se encarga de indexar sobre Solr los datos de las tablas de la aplicacion
en tiempo real cada vez que un nuevo dato es introducido o actualizado. Para ello,
antes se debe de indicar que tablas se desean indexar en Solr, bien de forma manual
a traves de la configuracion del esquema de Solr “schema.xml” para cada tabla o
a traves de la herramienta de lınea de comandos “dsetools”. En la figurera 7.3 se
muestra la arquitectura de DSE Search con los nodos de Cassandra y Solr.
Cuando una tabla es indexada sobre Solr, se crea un nuevo “campo solr quer”
en la tabla de Cassandra. Este campo permitira a la aplicacion crear sentencias de
consulta de busqueda avanzadas sobre los datos con uso de comodines y especifica-
66 Capıtulo 7
Figura 7.3: Arquitectura DSE Search con Cassandra y Solr
cion de los campos de busqueda. Incluye ademas opciones de ordenacion de datos,
paginacion y cacheado de las busqueda.
DSE Hadoop y DSE Spark
Apache Hadoop viene incluido por defecto en el paquete de instalacion de DataStax
Enterprise. Los datos de Cassandra son almacenados en un sistema de ficheros propio
llamado Cassandra File System o CFS. Sin embargo, se puede configurar para que
los datos sean almacenados en el sistemas de ficheros de Hadoop HDFS.
DataStax Enterprise Hadoop y Spark son herramientas ideales para el procesa-
miento y analisis de datos a gran escala. Por ello, se ha decidido no desplegar los
nodos DSE Hadoop y Spark ya que todavıa no se estan aprovechando su uso. Pos-
teriormente se podran hacer analisis sobre los datos de las imagenes y metadatos,
por ejemplo para la clasificacion o conversion de las imagenes, los metadatos y las
busquedas realizadas.
Diseno 67
7.1.2 Arquitectura REST
Existen varios tipos de servicios web para proveer una API (Applitation Program
Interface) que permita la integracion de una aplicacion de terceros con la generada.
Algunos servicios web son RPC (Remote Procedure Call), SOAP (Simple Object
Access Protocol) y REST (Representational State Transfer).
Los servicios web de arquitectura REST se consideran entidades que representan
conceptos para la logica de negocio de las aplicaciones. Al tratarse de una arqui-
tectura independiente del lenguaje, es perfecta para entornos web, movil o servicios
IoT (Internet of Things). Las ventajas de REST es que se trata de una arquitectura
sencilla de acceso a datos bajo peticiones del protocolo web HTTP y tiene mejor
escalabilidad y rendimiento que tecnologıas basadas en protocolos como SOAP.
Martin Fowler [Fowler, 2010] describe los diferentes niveles de servicios REST
segun el Modelo de Madurez RESTful de Richardson
[Richardson, Leonard and Ruby, Sam, 2007], explicando las propiedades especıficas
de un servicio web. El modelo es una buena manera de pensar sobre el uso buen uso
de tecnicas REST y diferenciar entre una arquitectura completamente RESTful.
En la figura 7.4 se muestran los 3 niveles de madurez, siendo el nivel cero un
servicio simple considerado insuficientes y el nivel 3 un servicio completo RESTful
“Hypermedia controls”.
Actualmente se esta trabajando mucho para construir en las organizaciones APIs
REST que expongan su negocio de forma sencilla para ser consumidos por diferentes
aplicaciones. Existen varios componentes para facilitar la creacion de servicios web
REST como Spring Data-Rest o Spring HATEOAS (Hypermedia as the Engine of
Application State).
La aplicacion integra una API RESTful de nivel 2 construida con Spring MVC
y clases propias de JHipster para la creacion de los datos de la cabecera en las
respuestas.
7.2 MODELO LOGICO DE DATOS
Se ha creado el modelo de datos de la aplicacion partiendo del diagrama de UML
de la fase de analisis para la elaboracion del modelo de datos. Al ser almacenados
68 Capıtulo 7
Figura 7.4: Niveles de madurez del servicio REST [Fowler, 2010].
los datos en Cassandra, para ofrecer el mejor rendimiento posible, el modelo debe
ser desnormalizado, duplicando tablas para diferentes propositos o creando ındices
y claves primarias agrupadas.
Las principales tablas de las que se compone la base de datos de la aplicacion se
encuentran a partir de la descripcion de la tabla 7.1. hasta la tabla 7.5.
Para el modelado de la arquitectura de tablas NoSQL se siguen las recomenda-
ciones de DataStax y otros artıculos de ejemplo como el descripto por Ebay en el
artıculo de Buenas practicas para el modelado de datos en Cassandra [Ebay, 2012].
Para obtener un buen rendimiento, se recomienda desnormalizar de los datos, du-
plicando tablas para propositos diferentes. En el trabajo, las imagenes de usuarios
se encuentran almacenadas en una unica tabla, sin embargo, una buena reestruc-
turacion serıa almacenar imagenes por usuario en caso de que se realizasen muchas
consultas con ese fin.
Se puede mejorar el rendimiento del modelo de datos mediante uso de claves
compuestas en el formato PRIMARIY KEY((ParticionKey1, PartitionKey2, etc),
ClusteringColumn1, ClusteringColumn2, etc). Por ejemplo, la clave primaria como
tal en Cassandra no tiene porque ser unica, sino que se refiere al particionado de da-
tos. Por tanto, tablas de asociaciones como “Metadata” se recomienda utilizar claves
Diseno 69
compuestas con uno o varios atributos para la Partition Key (picture id por ejem-
plo) y Clustering Column (clave de agrupacion de columnas) para el identificador,
tipo de dato de Metadata, etc. La columna de agrupacion afecta a la ordenacion. Se
debe de tener en cuenta que Cassandra obliga a actualizar los datos usando todos
los campos de la clave compuesta, sin embargo, para eliminar filas solo se solicita
las claves de particionado Partition Key.
Tabla user
Campo T. Dato Descripcion
id UUID (Partition Key) Id. de usuario
login TEXT Nombre de la cuenta de usuario
password TEXT Contrasena encriptada en blowfish
firstname TEXT Nombre propio
lastname TEXT Apellidos
email TEXT Correo electonico
activated BOOLEAN Usuario activo o inactivo
lang key TEXT Idioma de usario, por defecto ingles
reset key TEXT Clave para reinicio de contrasena
reset date TIMESTAMP Tiempo valido para el reinicio
authorities SET TEXT Lista de roles, por defecto usuario
Cuadro 7.1: Tabla de Usuarios
Tabla picture
Campo T. Dato Descripcion
id UUID (Partition Key) Id. de imagen
title TEXT Nombre de imagen
description TEXT Descripcion de imagen
pictureFile BLOB Imagen original
littlePictureFile BLOB Imagen escalada a 120px
mediumPictureFile BLOB Imagen escalada a 400px
bigPictureFile BLOB Imagen escalada a 1024px
created TIMESTAMP Fecha de creacion
modified TIMESTAMP Fecha de modificacion
owner TEXT Login de usuario del autor
modifiedBy TEXT Login de usuario del editor
autocolorCorrelogramAsBase64 TEXT Vector de caracterısticas en binario
autocolorCorrelogram List Double Vector de caracterısticas
ceddAsBase64 text TEXT Vector de caracterısticas en binario
cedd List Double Vector de caracterısticas
colorHistogramAsBase64 TEXT Vector de caracterısticas en binario
colorHistogram List Double Vector de caracterısticas
colorLayoutAsBase64 TEXT Vector de caracterısticas en binario
colorLayout List Double Vector de caracterısticas
edgeHistogramAsBase64 TEXT Vector de caracterısticas en binario
edgeHistogram List Double Vector de caracterısticas
phogAsBase64 TEXT Vector de caracterısticas en binario
phog List Double Vector de caracterısticas
Cuadro 7.2: Tabla de Imagenes
70 Capıtulo 7
Tabla metadata
Campo T. Dato Descripcion
picture id UUID (Partition Key) Id. de imagen
id UUID (Clustering Column) Id. de registro
directoryName TEXT Directorio de la metainformacion
tagType INT Tipo de etiquta
tagName TEXT Nombre de la etiqueta
description TEXT Valor de la metainformacion
title TEXT Nombre de la imagen
pictureFile BLOB Imagen escalada a 120px
Cuadro 7.3: Tabla de Metadatos
Tabla picturesearch
Campo T. Dato Descripcion
id UUID (Partition Key) Id. de imagen buscada
created TIMESTAMP (Clustering Column) Fecha de busqueda
pictureFile BLOB imagen de busqueda
littlePictureFile BLOB imagen escalada a 120px
userLogin TEXT usuario que realiza la busqueda
autocolorCorrelogramAsBase64 TEXT Vector de caracterısticas en binario
autocolorCorrelogram List Double Vector de caracterısticas
ceddAsBase64 text TEXT Vector de caracterısticas en binario
cedd List Double Vector de caracterısticas
colorHistogramAsBase64 TEXT Vector de caracterısticas en binario
colorHistogram List Double Vector de caracterısticas
colorLayoutAsBase64 TEXT Vector de caracterısticas en binario
colorLayout List Double Vector de caracterısticas
edgeHistogramAsBase64 TEXT Vector de caracterısticas en binario
edgeHistogram List Double Vector de caracterısticas
phogAsBase64 TEXT Vector de caracterısticas en binario
phog List Double Vector de caracterısticas
Cuadro 7.4: Tabla de Imagenes Buscadas
7.3 DISENO DE LA APLICACION
En esta seccion se describe el diseno de los principales componentes de la arquitec-
tura de la aplicacion. Para crear la aplicacion de lado servidor o back-end se utilizan
tecnologıas Java Spring Boot [Spring, 2015b]. El lado cliente, front-end, utiliza An-
gularJs [Google, 2015a] para realizar las peticiones contra el servidor y muestra la
vista en formato “resposive” con Twitter Bootstrap [Otto, 2015].
7.3.1 Modelo
En esta seccion se muestran los detalles de diseno e implementacion de la capa
modelo de la aplicacion.
Diseno 71
Tabla PictureFound
Campo T. Dato Descripcion
pictureSearch id UUID (Partition Key) Id. de imagen
totalScore FLOAT (Clustering Column) puntuacion total
picture id UUID Id. de imagen asociada
title TEXT Nombre de imagen
owner TEXT Login de usuario del autor
created TIMESTAMP Fecha de creacion
littlePictureFile BLOB Imagen escalada a 120px
mediumPictureFile BLOB Imagen escalada a 400px
bigPictureFile BLOB Imagen escalada a 1024px
autocolorCorrelogramScore FLOAT puntuacion recibida por Lucene
ceddScore FLOAT puntuacion recibida por Lucene
colorHistogramScore FLOAT puntuacion recibida por Lucene
colorLayoutScore FLOAT puntuacion recibida por Lucene
edgeHistogramScore FLOAT puntuacion recibida por Lucene
phogScore FLOAT puntuacion recibida por Lucene
Cuadro 7.5: Tabla de Imagenes Encontradas
Clases Persistentes
En la figura 7.5 se muestra un diagrama detallado con las entidades persistentes.
Diseno de la Capa de Acceso a Datos
En la siguiente figura7.6 se muestran las clases Repository e interfaces Accessor para
el acceso a los datos de las diferentes entidades del sistema.
Diseno de los Servicios
Se ha definido una separacion en dos servicios: “PictureService” y “PictureSearch-
Service”.
En la figura 7.7 se muestran las clases de los servicios descriptos.
72 Capıtulo 7
Figura 7.5: Diagrama de clases en UML.
Diseno 73
Figura 7.6: Diagrama de la Capa de Acceso a Datos.
74 Capıtulo 7
Figura
7.7:
Diagram
adela
Implementacion
delosServicios.
Diseno 75
Diagramas de Secuencia
En este apartado se exponen varios ejemplos de diagramas de secuencia para alma-
cenar, indexar y buscar las imagenes.
Diagrama de Secuencia Almacenar Imagen. Proceso de almacenamiento de
una imagen. Escalado de imagen en varios tamanos, extraccion y almacenamiento
de los metadatos, extraccion de caracterısticas e indexacion de los vectores de las
mismas.
En primer lugar se realizan las operaciones de creacion, actualizacion y elimi-
nacion de las imagenes. En el metodo de creacion de una imagen, se extraen las
caracterısticas y los metadatos. Las caracterısticas de imagen son almacenadas en
el servidor a traves de Lire mediante un metodo asıncrono, en segundo plano.
En la figura 7.8 se muestra el diagrama de secuencia del proceso de almacena-
miento, extraccion de los metadatos e indexacion de sus caracterısticas.
Diagrama de Secuencia Busqueda de Imagenes. Proceso de busqueda de
imagenes mediante una imagen fuente y almacenamiento de los resultados obtenidos.
El proceso utiliza la librerıa de Lire para realizar la busqueda a traves de los ındices
guardados en Lucene [Apache, 2015c].
En el servicio “PictureSearchService” se realizan las operaciones de busqueda de
imagenes. La operacion “searchByAllFeatures” busca en todos los ındices de des-
criptores de Lucene un lista de imagenes similares a la fuente de entrada. Para cada
descriptor, se almacena las imagenes encontradas y su puntuacion. La puntuacion
total, junto con la de cada descriptor, queda registrada en una lista de imagenes
encontradas de LirePictureSortable. La lista se ordena desde la menor puntuacion
(mayor similitud con la imagen de entrada) hasta la mayor (menos similitud). Las
imagenes obtenidas contienen solo el identificador de imagen que deben ser buscadas
en Cassandra a traves de “PictureRepository” con el metodo findAllByIds.
En la figura 7.9 se muestra el diagrama de secuencia del proceso de busqueda
por contenido de imagenes.
76 Capıtulo 7
Figura 7.8: Diagrama de Secuencia Almacenar Imagen
7.3.2 API Rest
La capa controlador es gestionada por Spring MVC [Spring, 2015c] para crear una
API RESTful. Cada operacion de la API llama a los servicios de la capa modelo y
las clases de la capa de acceso a datos (Repository) a traves de las entidades. Los
metodos reciben y envıan objetos DTO (Data Transfer Object) que representan a las
entidades persistentes de la capa modelo. Los DTO son mapeados con sus respectivas
entidades de forma automatica mediante el uso de la liberıa de Java MapStruct. En la
aplicacion se crear las interfaces “Mapper” de cada entidad y mediante la inyeccion
Diseno 77
Figura 7.9: Diagrama de Secuencia Busqueda de Imagenes
de dependencias de Spring se instancian las implementaciones creadas durante la
compilacion del proyecto Java. Por ello, cada vez que se modifique un DTO, se debe
compilar la aplicacion antes de su ejecucion. Es posible automatizar este proceso en
el IDE utilizado en el fichero de configuracion de Maven pom.xml. La forma mas
sencilla es ejecutar desde un terminal, en la ruta de la aplicacion el comando mvn
compile.
Los metodos REST devuelven los objetos DTO en formato JSON junto con una
cabecera indicando informacion adicional: datos de paginacion, siguiente lista de
elementos, numero total o nuevo elemento creado con su url.
78 Capıtulo 7
7.3.3 Aplicacion Lado Cliente o Front-End
La vista esta generada con AngularJs y Boostrap. Angular llama a la API RESTful
del servidor mediante la librerıa Angular-Resource [Google, 2015b]. En Angular se
define un modelo de la vista con los datos de las entidades descriptas como objetos
JSON. Cada entidad tiene asociada un servicio que llama a la API REST 7.3.2
mediante peticiones HTTP. Los controladores de Angular interactuan entre la vista
(HTML5) y el modelo para llamar al servicio y resolver las peticiones del usuario.
Mediante Bower se gestionan las dependencias de JavaScript anadidas al pro-
yecto. Grunt es el gestor de tareas y permite lanzar una servidor de aplicacion y
actualizar los cambios en los recursos web en caliente.
8IMPLEMENTACION Y PRUEBAS
8.1 IMPLEMENTACION
En este apartado se muestran detalles de estructura global de la aplicacion y la im-
plementacion de acciones importantes para algunas funcionalidades de la aplicacion.
8.1.1 Arquitectura Global de Paquetes
En la figura 8.1 se muestra la arquitectura global de paquetes de la aplicacion me-
diante un diagrama de paquetes en UML.
8.1.2 Cassandra y Solr
Para la creacion de las tablas se debe crean un KeySpace, similar al esquema de
datos en una base de datos relacional, que define la clase y el factor de replicacion
en Cassandra.
79
80 Capıtulo 8
Figura 8.1: Arquitectura global de paquetes de la aplicacion.
La definicion de las tablas se encuentran en formato CQL (Cassandra Query
Language) en la ruta del proyecto src/main/resources/config/cql/. A continuacion
se muestra el esquema de creacion de tablas.
Esquema de creacion de Tablas en CQL
A continuacion se muestran las sentencias de creacion del “KeySpace” y las tablas:
1 ## Flipper KeySpace , the name of application
2 CREATE KEYSPACE IF NOT EXISTS flipper
3 WITH REPLICATION = { ’class’ : ’SimpleStrategy ’, ’replication_factor ’ : 3 }
4 AND DURABLE_WRITES = true;
5
6 ## User Ttable ##
7 CREATE TABLE IF NOT EXISTS user (
8 id text ,
9 login text ,
10 password text ,
11 firstname text ,
12 lastname text ,
13 email text ,
14 activated boolean ,
15 lang_key text ,
16 activation_key text ,
17 reset_key text ,
18 reset_date timestamp ,
19 authorities set <text >,
20 PRIMARY KEY(id , created)
Implementacion y pruebas 81
21 );
22
23 ## To search users ##
24 CREATE TABLE IF NOT EXISTS user_by_login (
25 login text ,
26 id text ,
27 PRIMARY KEY(login , id)
28 );
29
30 CREATE TABLE IF NOT EXISTS user_by_email (
31 email text ,
32 id text ,
33 PRIMARY KEY(email , id)
34 );
35
36 CREATE TABLE IF NOT EXISTS user_by_activation_key (
37 activation_key text ,
38 id text ,
39 PRIMARY KEY(activation_key , id)
40 );
41
42 CREATE TABLE IF NOT EXISTS user_by_reset_key (
43 reset_key text ,
44 id text ,
45 PRIMARY KEY(reset_key , id)
46 );
47
48 CREATE TABLE IF NOT EXISTS activation_key_by_creation_date (
49 creation_date timeuuid ,
50 activation_key text ,
51 PRIMARY KEY(creation_date , activation_key)
52 );
53
54 ## Token table for API REST access ##
55 CREATE TABLE IF NOT EXISTS persistent_token (
56 series text ,
57 token_date timestamp ,
58 user_agent text ,
59 token_value text ,
60 login text ,
61 user_id text ,
62 ip_address text ,
63 PRIMARY KEY(series)
64 );
65
66 CREATE TABLE IF NOT EXISTS persistent_token_by_user (
67 user_id text ,
68 persistent_token_series text ,
69 PRIMARY KEY(user_id)
70 );
71
72 ## Picture table ##
73 CREATE TABLE IF NOT EXISTS picture (
74 id uuid ,
75 title text ,
76 description text ,
77 pictureFile blob ,
78 littlePictureFile blob ,
79 mediumPictureFile blob ,
80 bigPictureFile blob ,
81 created timestamp ,
82 modified timestamp ,
83 owner text ,
84 modifiedBy text ,
85 autocolorCorrelogramAsBase64 text ,
86 autocolorCorrelogram list <double >,
87 ceddAsBase64 text ,
88 cedd list <double >,
82 Capıtulo 8
89 colorHistogramAsBase64 text ,
90 colorHistogram list <double >,
91 colorLayoutAsBase64 text ,
92 colorLayout list <double >,
93 edgeHistogramAsBase64 text ,
94 edgeHistogram list <double >,
95 phogAsBase64 text ,
96 phog list <double >,
97 PRIMARY KEY((id), created)
98 ) WITH CLUSTERING ORDER BY (created DESC)
99 AND compaction = { ’class’:’LeveledCompactionStrategy ’ }
100 AND comment=’A table to hold picture ’;
101
102 CREATE INDEX ON picture ( owner );
103
104 ## Metadatos ##
105 CREATE TABLE IF NOT EXISTS metadata (
106 picture_id uuid ,
107 id uuid ,
108 directoryName text ,
109 tagType int ,
110 tagName text ,
111 description text ,
112 title text ,
113 pictureFile BLOB ,
114 PRIMARY KEY(( picture_id), id)
115 ) WITH CLUSTERING ORDER BY (id DESC)
116 AND compaction = { ’class’:’LeveledCompactionStrategy ’ }
117 AND comment=’A table to hold metadata from pictures ’;
118
119 ## PictureSearch ##
120 CREATE TABLE IF NOT EXISTS pictureSearch (
121 id uuid ,
122 pictureFile blob ,
123 littlePictureFile blob ,
124 created timestamp ,
125 userLogin text ,
126 pictureIdList list <uuid >,
127 autocolorCorrelogramAsBase64 text ,
128 autocolorCorrelogram list <double >,
129 ceddAsBase64 text ,
130 cedd list <double >,
131 colorHistogramAsBase64 text ,
132 colorHistogram list <double >,
133 colorLayoutAsBase64 text ,
134 colorLayout list <double >,
135 edgeHistogramAsBase64 text ,
136 edgeHistogram list <double >,
137 phogAsBase64 text ,
138 phog list <double >,
139 PRIMARY KEY((id), created)
140 ) WITH CLUSTERING ORDER BY (created DESC)
141 AND compaction = { ’class’:’LeveledCompactionStrategy ’ }
142 AND comment=’A table to hold picture searchs ’;
143
144 CREATE INDEX ON picturesearch ( userLogin );
145
146 ## PictureFound ##
147 CREATE TABLE IF NOT EXISTS pictureFound (
148 pictureSearch_id uuid ,
149 picture_id uuid ,
150 title text ,
151 owner text ,
152 created timestamp ,
153 littlePictureFile blob ,
154 mediumPictureFile blob ,
155 bigPictureFile blob ,
156 totalScore float ,
Implementacion y pruebas 83
157 autocolorCorrelogramScore float ,
158 ceddScore float ,
159 colorHistogramScore float ,
160 colorLayoutScore float ,
161 edgeHistogramScore float ,
162 phogScore float ,
163 PRIMARY KEY(( pictureSearch_id), totalscore)
164 ) WITH CLUSTERING ORDER BY (totalscore ASC)
165 AND compaction = { ’class’:’LeveledCompactionStrategy ’ }
166 AND comment=’A table to hold found pictures from searchs ’;
167
168 CREATE INDEX ON pictureFound ( picture_id );
169 CREATE INDEX ON pictureFound ( owner );
Configuracion de Solr
Para utilizar Solr como motor de busquedas de los datos de Cassandra (Metata-
dos, imagenes y los datos de busqueda de imagenes) se debe configurar Solr acorde
a los datos que seran indexados. Para ello se utiliza DataStax Enterprise Search
[DataStax, 2015c] con soporte para la indexacion automatica de las tablas de Cas-
sandra en caliente.
Para realizar la configuracion automatica se incluye en el proyecto, ruta solr config
el script automaticly create schema indexes.sh con el uso de la herramienta en lınea
de comandos dsetools de DSE Search.
#!/ bin/bash
sudo dsetool create_core flipper.picture generateResources=true
reindex=true coreOptions=config -picture.yaml
sudo dsetool create_core flipper.metadata generateResources=true
reindex=true coreOptions=config -metadata.yaml
sudo dsetool create_core flipper.picturesearch generateResources=
true reindex=true coreOptions=config -pictureSearch.yaml
sudo dsetool create_core flipper.picturefound generateResources=
true reindex=true coreOptions=config -pictureFound.yaml
Los datos indexados se pueden ver desde el panel de administracion de Solr
y pueden ser consultados a traves de la API REST que despliega en el puerto
TCP/8983. En la figura 8.2 se muestra el panel de administracion de Solr para la
busqueda de los metadados de la aplicacion.
Para optimizar las busquedas de los datos en Cassandra con Solr se debe deshabi-
litar la paginacion por defecto. En el fichero de configuracion “CassandraProperties”:
1 private int fetchSize = Integer.MAX_VALUE;
2
3 private String compression = ProtocolOptions.Compression.LZ4.name();
84 Capıtulo 8
Figura 8.2: Panel de administracion de Solr.
8.1.3 Modelo
El modelo implementa la gestion de la persistencia de datos y las distintas funcio-
nalidades de la aplicacion. Recibe peticiones del controlador, con las que almacena
o devuelve informacion de la base de datos.
Entidades
Cada tabla de la base de datos esta mapeada como un objeto relacional con las
clases entidad a traves del Driver de Java para Cassandra de DataStax. El diagrama
de clases del capıtulo de diseno muestra la figura del diagrama de clases de UML.
Para realizar el mapeo contra la base de datos con Cassandra utiliza el sistema de
anotaciones muy similar al ORM/Hibernate: La anotacion @Table permite indicar
la tabla asociada a la clase entidad y @NotNull, @Size o @Pattern permite indicar
opciones de validacion en los atributos. Ademas existen anotaciones que permiten
configurar el mapeo con la base de datos NoSQL como @PartitionKey, clave primario
de particion de datos, o @ClusteringColum, como clave de agrupacion de columnas.
A continuacion se muestra el codigo de la creacion de las entidades de Picture y
Implementacion y pruebas 85
Metadata.
1 @Table(name = "picture")
2 public class Picture implements Serializable {
3
4 @PartitionKey
5 private UUID id;
6
7 @NotNull
8 @Size(min = 3, max = 100)
9 private String title;
10
11 private String description;
12
13 @NotNull
14 private ByteBuffer pictureFile;
15 private ByteBuffer littlePictureFile;
16 private ByteBuffer mediumPictureFile;
17 private ByteBuffer bigPictureFile;
18
19 private String autocolorCorrelogramAsBase64;
20 private List <Double > autocolorCorrelogram;
21 private String ceddAsBase64;
22 private List <Double > cedd;
23 private String colorHistogramAsBase64;
24 private List <Double > colorHistogram;
25 private String colorLayoutAsBase64;
26 private List <Double > colorLayout;
27 private String edgeHistogramAsBase64;
28 private List <Double > edgeHistogram;
29 private String phogAsBase64;
30 private List <Double > phog;
31
32 @NotNull
33 private String owner;
34
35 private String modifiedBy;
36
37 @NotNull
38 private Date created;
39
40 private Date modified;
41 }
1 @Table(name = "metadata")
2 public class Metadata implements Serializable {
3 @PartitionKey
4 private UUID picture_id;
5
6 @ClusteringColumn
7 private UUID id;
8
9 @NotNull
10 private String directoryName;
11
12 @NotNull
13 private Integer tagType;
14
15 @NotNull
16 private String tagName;
17
18 @NotNull
19 @Pattern(regexp = "^[^<> %$]*$")
20 private String description;
21
22 @Size(min = 3, max = 100)
86 Capıtulo 8
23 private String title;
24
25 private ByteBuffer pictureFile;
26 }
Repository y Accessor
La capa de acceso a datos de cada entidad se desarrolla en un conjunto de clase
dentro del paquete “repository” de la aplicacion. La version de Cassandra distribuida
por DataStax incluye el driver de Java que permite realizar consultar y crear clases
“Repository” mediante un mapeador con las tablas para operaciones CRUD. Sin
embargo, tambien es importante poder realizar sentencias mas personalizadas y
agrupar conjuntos operaciones de datos por lotes. Esto es posible gracias a la interfaz
“Accesor” y a las clases “PrepareStament” y “BatchStamtent”.
La clase “com.datastax.driver.mapping.Mapper” se debe instanciar a traves del
objeto de sesion de la conexion con Cassadra (mediante la inyeccion de dependencias
de Spring) y permite realizar operaciones CRUD 1. Las clases “PrepareStament” y
“BatchStamtent” permiten realizar operaciones mas complejas contra las tablas de
Cassandra mediante el lenjuage CQL (Cassandra Query Languaje) a traves del envıo
de parametros y obtencion de datos.
En las siguientes lıneas de codigo se muestra la implementacion de la clase repo-
sitorio para la entidad “Picture”.
1 /**
2 * Cassandra repository for the Picture entity.
3 */
4 @Repository
5 public class PictureRepository {
6
7 private final Logger log = LoggerFactory.getLogger(PictureRepository.class);
8
9 @Inject
10 private Session session;
11
12 private Mapper <Picture > mapper;
13
14 private PictureAccessor pictureAccesor;
15
16 private PreparedStatement findAllStmt;
17
18 private PreparedStatement truncateStmt;
19
20 @PostConstruct
21 public void init() {
22 MappingManager manager = new MappingManager (session);
23 pictureAccesor = manager.createAccessor(PictureAccessor.class);
1CRUD: Create, Read, Update and Delete.
Implementacion y pruebas 87
24 mapper = manager.mapper(Picture.class);
25 findAllStmt = session.prepare("SELECT * FROM picture");
26 truncateStmt = session.prepare("TRUNCATE picture");
27 }
28
29 public List <Picture > findByOwner(String owner){
30 return pictureAccesor.findByOwner(owner).all();
31 }
32
33 public List <Picture > findAll () {
34 return pictureAccesor.findAll ().all();
35 }
36
37
38 /**
39 * Using solr_query with start and count for manual paginating
40 * Needs disable
41 * @param pageable
42 * @return
43 */
44 public Page <Picture > findAllOrdered(Pageable pageable) {
45
46 String query = String.format("{\"q\":\"*:*\" , \" start \": %s, \"sort \":\" created
DESC \"}", pageable.getOffset ());
47 List <Picture > pictures = pictureAccesor.findAllOrdered(query , pageable.
getPageSize ()).all();
48
49 String queryLimit = String.format("{\"q\":\"*:*\" , \" start \": %s, \"sort \":\"
created DESC \"}", pageable.getOffset () + 1);
50 int total = (pictureAccesor.findAllOrdered(queryLimit , 1).all().size() == 0) ?
0 : 100;
51
52 return new PageImpl <>(pictures , pageable , total);
53 }
54
55 public List <Picture > findByIdsList(List <UUID > idsList) {
56
57 List <Picture > pictures = new ArrayList <>(idsList.size());
58
59 List <String > idsAsString = new ArrayList <>(idsList.size());
60 idsAsString.addAll(idsList.stream ().map(UUID:: toString).collect(Collectors.
toList ()));
61
62 PreparedStatement statement = session.prepare("SELECT * from picture WHERE
id IN ?;");
63 session.execute(statement.bind(idsList)).all().stream ().map(
64 row -> {
65 Picture picture = new Picture ();
66 picture.setId(row.getUUID("id"));
67 picture.setTitle(row.getString("title"));
68 picture.setDescription(row.getString("description"));
69 picture.setOwner(row.getString("owner"));
70 picture.setModifiedBy(row.getString("modifiedBy"));
71 picture.setPictureFile(row.getBytes("pictureFile"));
72 picture.setLittlePictureFile(row.getBytes("littlePictureFile"));
73 picture.setMediumPictureFile(row.getBytes("mediumPictureFile"));
74 picture.setBigPictureFile(row.getBytes("bigPictureFile"));
75 picture.setCreated(row.getDate("created"));
76 picture.setModified(row.getDate("modified"));
77 picture.setAutocolorCorrelogramAsBase64(row.getString("
autocolorCorrelogramAsBase64"));
78 picture.setAutocolorCorrelogram(row.getList("autocolorCorrelogram",
Double.class));
79 picture.setCeddAsBase64(row.getString("ceddAsBase64"));
80 picture.setCedd(row.getList("cedd", Double.class));
81 picture.setColorHistogramAsBase64(row.getString("
colorHistogramAsBase64"));
82 picture.setColorHistogram(row.getList("colorHistogram", Double.
88 Capıtulo 8
class));
83 picture.setColorLayoutAsBase64(row.getString("colorLayoutAsBase64")
);
84 picture.setColorLayout(row.getList("colorLayout", Double.class));
85 picture.setEdgeHistogramAsBase64(row.getString("
edgeHistogramAsBase64"));
86 picture.setEdgeHistogram(row.getList("edgeHistogram", Double.class)
);
87 picture.setPhogAsBase64(row.getString("phogAsBase64"));
88 picture.setPhog(row.getList("phog", Double.class));
89 return picture;
90 }
91 ).forEach(pictures ::add);
92
93 return pictures;
94 }
95
96 public Picture findOne(UUID id) {
97 return mapper.get(id);
98 }
99
100 public List <Picture > search(String query){
101 return pictureAccesor.search(query).all();
102 }
103
104 public Picture save(Picture picture) {
105 if (picture.getId () == null) {
106 picture.setId(UUID.randomUUID ());
107 }
108
109 mapper.save(picture);
110 return picture;
111 }
112
113 public void delete(UUID id) {
114 mapper.delete(id);
115 }
116
117 public void deleteAll () {
118 BoundStatement stmt = truncateStmt.bind();
119 session.execute(stmt);
120 }
121 }
Interfaz Accessor
La interfaz “Accessor” asociada, permite personalizar consultas CQL mas avanzadas.
La interfaz de manera similar a una clase JPA (Java Persistence API) y Spring Data
JPA2. Cuando se compila la aplicacion, las anotaciones indicadas en la cabeceara de
los metodos son interpretadas por el driver de DataStax para generar el codigo que
realizar las llamadas a la base de datos de Cassandra. A continuacion se muestra la
interfaz de Picture y Metadata.
1 @Accessor
2 public interface PictureAccessor {
3
2http://projects.spring.io/spring-data-jpa/
Implementacion y pruebas 89
4 @Query("SELECT * FROM picture WHERE id = :id")
5 Picture findOne(@Param("id") UUID id);
6
7 @Query("SELECT * FROM picture WHERE solr_query = :query LIMIT :total")
8 Result <Picture > findAllOrdered(@Param("query") String query , @Param("total") int total)
;
9
10 @Query("SELECT * FROM picture LIMIT 10000")
11 Result <Picture > findAll ();
12
13 @Query("SELECT * FROM picture WHERE owner = :owner LIMIT 10000")
14 Result <Picture > findByOwner(@Param("owner") String owner);
15
16 @Query("SELECT * FROM picture WHERE solr_query = :query")
17 Result <Picture > search(@Param("query") String query);
18 }
1 @Accessor
2 public interface MetadataAccessor {
3
4 @Query("SELECT * FROM metadata WHERE id = :id")
5 Metadata findOne(@Param("id") UUID id);
6
7 @Query("SELECT * FROM metadata LIMIT 10000")
8 Result <Metadata > findAll ();
9
10 @Query("SELECT * FROM metadata WHERE picture_id = :picture_id LIMIT 10000")
11 Result <Metadata > findByPicture_id(@Param("picture_id") UUID picture_id);
12
13 @Query("UPDATE metadata SET title = :title WHERE id = :id AND picture_id = :picture_id"
)
14 void updateByPicture_id(@Param("id") UUID id, @Param("picture_id") UUID picture_id ,
@Param("title") String title);
15
16 @Query("DELETE FROM metadata WHERE picture_id = :picture_id")
17 void deleteByPicture_id(@Param("picture_id") UUID picture_id);
18
19 @Query("SELECT * FROM metadata WHERE solr_query = :query")
20 Result <Metadata > search(@Param("query") String query);
21 }
Optimizacion de inserciones multiples
Es posible lanzar una unica peticion de insercion multiple de datos con el Driver
de Java de DataStax. A continuacion se muestra un ejemplo para almacenar los
metadatos de una imagen.
1 public void saveAll(Set <Metadata > metadataSet , Picture picture)
2 {
3 UUID picture_id = picture.getId ();
4 String title = picture.getTitle ();
5 ByteBuffer pictureFile = picture.getLittlePictureFile ();
6
7 PreparedStatement ps = session.prepare("INSERT INTO metadata (" +
8 "id," +
9 "directoryName ," +
10 "tagType ," +
11 "tagName ," +
12 "description ," +
13 "picture_id ," +
90 Capıtulo 8
14 "title ," +
15 "pictureFile" +
16 ") VALUES (?, ?, ?, ?, ?, ?, ?, ?)");
17
18 BatchStatement batch = new BatchStatement ();
19 for(Metadata metadata : metadataSet)
20 {
21 batch.add(ps.bind(UUID.randomUUID (),
22 metadata.getDirectoryName (), metadata.getTagType (), metadata.getTagName (), metadata.getDescription
(),
23 picture_id , title , pictureFile));
24 }
25
26 /* execute batch multiple insert */
27 session.execute(batch);
28 }
8.1.4 Servicios
Los servicios principales de la aplicacion son “PictureService” y “PictureSearchSer-
vice”, para el almacenamiento e indexacion de las imagenes y para las busquedas
avanzadas.
PictureService
Para la subida y almacenamiento de imagenes se llama al metodo create del servicio,
el cual crea un nuevo objeto de tipo “Picture” con los datos recibidos por el DTO
(Data Transfer Object) en la capa controlador. La imagen se recibe en forma de
vector de tipo byte, es almacenado en la base de datos como otro atributo mas de
la imagen y se persiste en la base de datos. A continuacion se realiza la llamada a
una serie de varios metodos asıncronos para que sean ejecutados en segundo plano:
1. Escalar Imagen: la imagen se escalada para ser almacenada en varios tamanos:
pequeno, mediano y grande. Ademas se almacena la imagen original enviada,
aunque esta no sera mostrada en la aplicacion.
2. Almacenar Metadatos: los metadatos de la imagen son extraıdos mediante la
clase “MetadataExtractionUtils” y se almacena la lista de todos los metadatos
de la imagen (operacion saveAll de la clase “MetadataRepository”).
3. Extraer caracterısticas: Se obtienen seis caracterısticas distintas de los descrip-
tores de imagen con Lire y son indexados con el identificador de imagen en
Lucene. Ademas, los vectores de estas caracterısticas se almacenan en la base
de datos de Cassandra, junto con las imagenes.
Implementacion y pruebas 91
En las siguientes lıneas de codigo se muestran los metodos del servicio imple-
mentados para almacenamiento y obtener la informacion de una imagen.
1 /**
2 * Create new picture with title , description and file
3 *
4 * @param picture
5 * @return
6 */
7 public Picture create(Picture picture) throws ImageProcessingException , IOException
{
8
9 /* set picture date */
10 picture.setCreated(new Date());
11
12 /* set owner */
13 picture.setOwner(SecurityUtils.getCurrentLogin ());
14
15 /* Save */
16 pictureRepository.save(picture);
17
18 /* start async process */
19
20 /* Scale images */
21 scaleImage(picture);
22
23 /* Create all features */
24 createFeatures(picture);
25
26 /* Create all metadata */
27 createMetadata(picture);
28
29 return picture;
30 }
31
32 /**
33 * Scale pictures
34 * @param picture
35 * @throws IOException
36 */
37 @Async
38 private void scaleImage(Picture picture) throws IOException {
39
40 ByteArrayInputStream in = new ByteArrayInputStream(picture.
getPictureFile ().array ());
41 BufferedImage image = ImageIO.read(in);
42
43 /* little */
44 if (Math.max(image.getHeight (), image.getWidth ()) > PictureScaleUtil.
LITTLE_SIZE) {
45 BufferedImage littleImg = ImageUtils.scaleImage(image ,
PictureScaleUtil.LITTLE_SIZE);
46 picture.setLittlePictureFile(ByteBuffer.wrap(PictureScaleUtil.
setBufferedImage(littleImg)));
47 }else {
48 picture.setLittlePictureFile(ByteBuffer.wrap(picture.
getPictureFile ().array ()));
49 }
50
51 /* medium */
52 if (Math.max(image.getHeight (), image.getWidth ()) > PictureScaleUtil.
MEDIUM_SIZE) {
53 BufferedImage littleImg = ImageUtils.scaleImage(image ,
PictureScaleUtil.MEDIUM_SIZE);
54 picture.setMediumPictureFile(ByteBuffer.wrap(PictureScaleUtil.
setBufferedImage(littleImg)));
92 Capıtulo 8
55 }else {
56 picture.setMediumPictureFile(ByteBuffer.wrap(picture.
getPictureFile ().array ()));
57 }
58
59 /* big */
60 if (Math.max(image.getHeight (), image.getWidth ()) > PictureScaleUtil.
BIG_SIZE) {
61 BufferedImage littleImg = ImageUtils.scaleImage(image ,
PictureScaleUtil.BIG_SIZE);
62 picture.setBigPictureFile(ByteBuffer.wrap(PictureScaleUtil.
setBufferedImage(littleImg)));
63 }else {
64 picture.setBigPictureFile(ByteBuffer.wrap(picture.
getPictureFile ().array ()));
65 }
66
67 /* update picture */
68 pictureRepository.save(picture);
69 }
70
71 /**
72 * Extract and create feature for picture
73 *
74 * @param picture
75 */
76 @Async
77 private void createFeatures(Picture picture) throws IOException {
78
79 // Index in Lucene
80 LireBuilder.index(picture.getPictureFile ().array (), picture.getId (),
lireConfiguration.getConf ());
81
82 Feature autocolor = FeatureExtractorBuilder.extract(picture.getPictureFile
().array (), FeatureEnumerate.AutoColorCorrelogram);
83 picture.setAutocolorCorrelogram(autocolor.getFeature ());
84 picture.setAutocolorCorrelogramAsBase64(autocolor.getFeatureAsBase64 ());
85
86 Feature CEDD = FeatureExtractorBuilder.extract(picture.getPictureFile ().
array (), FeatureEnumerate.CEDD);
87 picture.setCedd(CEDD.getFeature ());
88 picture.setCeddAsBase64(CEDD.getFeatureAsBase64 ());
89
90 Feature ColorHistogram = FeatureExtractorBuilder.extract(picture.
getPictureFile ().array (), FeatureEnumerate.ColorHistogram);
91 picture.setColorHistogram(ColorHistogram.getFeature ());
92 picture.setColorHistogramAsBase64(ColorHistogram.getFeatureAsBase64 ());
93
94 Feature ColorLayout = FeatureExtractorBuilder.extract(picture.
getPictureFile ().array (), FeatureEnumerate.ColorLayout);
95 picture.setColorLayout(ColorLayout.getFeature ());
96 picture.setColorLayoutAsBase64(ColorLayout.getFeatureAsBase64 ());
97
98 Feature EdgeHistogram = FeatureExtractorBuilder.extract(picture.
getPictureFile ().array (), FeatureEnumerate.EdgeHistogram);
99 picture.setEdgeHistogram(EdgeHistogram.getFeature ());
100 picture.setEdgeHistogramAsBase64(EdgeHistogram.getFeatureAsBase64 ());
101
102 Feature PHOG = FeatureExtractorBuilder.extract(picture.getPictureFile ().
array (), FeatureEnumerate.PHOG);
103 picture.setPhog(PHOG.getFeature ());
104 picture.setPhogAsBase64(PHOG.getFeatureAsBase64 ());
105
106 /* update picture */
107 pictureRepository.save(picture);
108 }
109
110 /**
Implementacion y pruebas 93
111 * Extract and create Metadata
112 *
113 * @param picture
114 * @throws ImageProcessingException
115 * @throws IOException
116 */
117 @Async
118 private void createMetadata(Picture picture) throws ImageProcessingException ,
IOException {
119
120 Set <Metadata > metadataSet = MetadataExtactorUtils.readMetadata(picture.
getPictureFile ().array ());
121
122 if (metadataSet.size() > LIMIT_PER_WRITE) {
123 Set <Metadata >[] subsets = new Set[MAX_WRITES ];
124 Metadata [] array = metadataSet.toArray(new Metadata[metadataSet
.size()]);
125
126 for (int i = 1; i <= MAX_WRITES; i++) {
127 int fromIndex = (i - 1) * metadataSet.size() / MAX_WRITES;
128 int toIndex = i * metadataSet.size() / MAX_WRITES - 1;
129 Set <Metadata > subHashSet = new HashSet <>();
130 subHashSet.addAll(Arrays.asList(Arrays.copyOfRange(array ,
fromIndex , toIndex)));
131
132 subsets[i - 1] = subHashSet;
133 }
134
135 // Avoid OutOfBand in Heap memory
136 for (Set <Metadata > subset : subsets) {
137 metadataRepository.saveAll(subset , picture);
138 }
139 } else {
140 metadataRepository.saveAll(metadataSet , picture);
141 }
142
143 }
El metodo createMetadata debe tener limitado el total de metadatos enviados
a la operacion en lotes de insercion en el metodo saveAll del repositorio. Esto es
necesario para que no se sobrepase el lımite de tamano de memoria soportado por
la clase “BatchStatement”. En su lugar, si el maximo de inserciones se sobrepasa se
divide en varios procesos saveAll.
Para la indexacion de caracterısticas se implementa la clase “LireBuilder”, con
metodos estaticos para extraer e indexar las caracterısticas de las imagenes en Lucene
a traves de la liberıa Lire [Lux, Mathias and Chatzichristofis, Savvas A., 2015a].
El metodo LireBuilder.index se encarga de extraer e indexar las caracterısti-
cas mediante constructores de cada descriptor Lire. Los descriptores utilizados en
la aplicacion son: AutoColorCorrelogram, CEDD, MPEG-7 ColorLayour, MPEG-7
EdgeHistogram, RGB ColorHistogram y PHOG descriptos en el capıtulo de tecno-
logıa, en la seccion 3.4.
1 public static void index(byte[] source , UUID picture_id , IndexWriterConfig conf) throws
IOException
94 Capıtulo 8
2 {
3 ByteArrayInputStream in = new ByteArrayInputStream(source);
4 BufferedImage image = ImageIO.read(in);
5
6 // Creating an Lucene IndexWriter
7 log.debug("Is Lucene configured? " + (conf == null));
8 if(conf == null) {
9 conf = new IndexWriterConfig(LuceneUtils.LUCENE_VERSION , new
WhitespaceAnalyzer(LuceneUtils.LUCENE_VERSION));
10 conf.setOpenMode(IndexWriterConfig.OpenMode.CREATE_OR_APPEND);
11 }
12
13 luceneIndexer(image , picture_id , FeatureEnumerate.AutoColorCorrelogram.getText
(), DocumentBuilderFactory.getAutoColorCorrelogramDocumentBuilder (), conf)
;
14 luceneIndexer(image , picture_id , FeatureEnumerate.CEDD.getText (),
DocumentBuilderFactory.getCEDDDocumentBuilder (), conf);
15 luceneIndexer(image , picture_id , FeatureEnumerate.ColorLayout.getText (),
DocumentBuilderFactory.getColorLayoutBuilder (), conf);
16 luceneIndexer(image , picture_id , FeatureEnumerate.EdgeHistogram.getText (),
DocumentBuilderFactory.getEdgeHistogramBuilder (), conf);
17 luceneIndexer(image , picture_id , FeatureEnumerate.ColorHistogram.getText (),
DocumentBuilderFactory.getColorHistogramDocumentBuilder (), conf);
18 luceneIndexer(image , picture_id , FeatureEnumerate.PHOG.getText (),
DocumentBuilderFactory.getPHOGDocumentBuilder (), conf);
19 }
20
21 /**
22 * Index for each builder type
23 * @param image
24 * @param picture_id
25 * @param prefix
26 * @param builder
27 * @param conf
28 * @throws IOException
29 */
30 private static void luceneIndexer(BufferedImage image , UUID picture_id , String
prefix , DocumentBuilder builder , IndexWriterConfig conf)
31 throws IOException
32 {
33 File path = getPath(prefix);
34 log.debug("creating indexed path " + path.getAbsolutePath ());
35 IndexWriter iw = new IndexWriter(FSDirectory.open(path), conf);
36
37 Document document = null;
38 try {
39 document = builder.createDocument(image , picture_id.toString ());
40 iw.addDocument(document);
41
42 } catch (Exception e) {
43 System.err.println("Error reading image or indexing it.");
44 e.printStackTrace ();
45 }
46
47 // closing the IndexWriter
48 iw.close();
49 }
PictureServiceSearch
El servicio PictureSearchService realiza las operaciones de busqueda avanzada de
imagenes a traves de contenido. Para optimizar los busquedas, estas son almacenadas
Implementacion y pruebas 95
de forma persisten en la base de datos. De este modo, los resultados podran ser
analizados posteriormente, comprobar los resultados obtenidos o mostrar las ultimas
busquedas realizadas por los usuarios.
El metodo searchByAllFeatures realiza la busqueda sobre el ındice de Luce-
ne con la librerıa de Lire. En primer lugar se llama al metodo estatico LireBuil-
der.searchAllFeatures que recibe el contenido en binario de la imagen de entrada y
el maximo numero de documentos que se desea obtener de busqueda (por defecto
100).
A continuacion se muestra el proceso de busqueda en “LireBuilder”.
1 /**
2 * Search pictures that appear in all features
3 *
4 * @param source
5 * @return
6 * @throws IOException
7 */
8 public static List <LirePictureSortable > searchAllFeatures(byte[] source , int maxHist)
throws IOException {
9
10 /* extract pictures sortable info from Lucene indexes */
11 List <LirePictureSortable > result= new ArrayList <>();
12 for(FeatureEnumerate feature : FeatureEnumerate.values ()){
13 result.addAll(LireBuilder.search(source , maxHist , feature , result));
14 }
15
16 /* save UUID from pictures ordered by score */
17 Collections.sort(result);
18
19 return result;
20 }
21
22 /**
23 * Search a picture
24 *
25 * @param imageBytes
26 * @param maxHits
27 * @param feature
28 * @param pictures
29 * @return
30 * @throws IOException
31 */
32 public static List <LirePictureSortable > search(byte[] imageBytes , int maxHits ,
FeatureEnumerate feature ,
33 List <LirePictureSortable > pictures) throws IOException{
34
35 File path = getPath(feature.getText ());
36 log.debug("reading from indexed path " +path.getAbsolutePath ());
37
38 IndexReader ir = DirectoryReader.open(FSDirectory.open(path));
39 ImageSearcher searcher = new GenericFastImageSearcher(maxHits , feature.
getValueClass ());
40
41 // searching with a image file ...
42 InputStream in = new ByteArrayInputStream(imageBytes);
43 ImageSearchHits hits = searcher.search(in , ir);
44
45 List <LirePictureSortable > result = new ArrayList <>();
46 float score = 0.0F;
96 Capıtulo 8
47 for (int i = 0; i < hits.length (); i++) {
48 score = hits.score(i);
49 LirePictureSortable lp = new LirePictureSortable(
50 UUID.fromString(hits.doc(i).getValues(DocumentBuilder.
FIELD_NAME_IDENTIFIER)[0]),
51 score , feature);
52
53 /* check is its a picture was repeated by picture UUID */
54 if(pictures.contains(lp))
55 {
56 lp = pictures.get(pictures.indexOf(lp));
57 lp.addDescriptor(feature , score);
58 lp.addScore(score);
59 }else {
60 result.add(lp);
61 }
62 }
63
64 return result;
65 }
El metodo de busqueda searchByAllFeatures realizar una busqueda de documen-
tos similares (vectores de descriptores de todas las imagenes) en los ındices de Luce-
ne, llamando al metodo search con cada descriptor soportado por Lire. Finalmente
se obtiene la lista de documentos encontrados a partir de la clase “GenericFastI-
mageSearcher” de Lire. Esta clase recibe como parametros de entrada el total de
documentos a devolver y el descriptor a buscar en el ındice (Lire extrae el vector de
la imagen de entrada y busca documentos similares).
El metodo search de la clase de Lire devuelve una lista de documentos ordenada
por puntuacion junto con el identificador almacenado de la imagen (Id. de la base de
datos). Esta puntuacion, dada por el motor de Lucene, refleja el grado de similitud
basada en los ındices de espacios metricos con archivos invertidos descripto en el
capıtulo de tecnologıa en la seccion de busquedas con Lire3.4.2. Cuanto menor sea
la puntuacion, mayor el grado de similitud. Lucene siempre va a devolver al menos el
numero de documentos solicitados, pero con una puntuacion mayor cuanto el vector
de entrada mas se aleje del ındice.
Por cada documento devuelto, se almacena en una instancia “LirePictureSorta-
ble”, una lista de descriptores junto con su puntuacion (objetoMap¡FeatureEnumerate,
Float)) y la suma de puntuacion total. Las instancias de los documentos encontrados
se guardan en una lista ordenada por la puntuacion total.
En el servicio se devuelve esta lista ordenada con todas las puntuaciones y se al-
macena de forma persistente en la entidad “PictureFound”. En el siguiente fragmento
de codigo se muestran los metodos asıncronos de su creacion y almacenamiento a
partir de los documentos encontrados.
Implementacion y pruebas 97
1
2 @Async
3 private List <PictureFound > createPicturesFound(PictureSearch search , List <LirePictureSortable >
pictureIdsList) {
4
5 List <PictureFound > result = new ArrayList <>();
6 List <UUID > uuids = pictureIdsList.stream ().map(LirePictureSortable :: getId).collect(Collectors.
toList ());
7
8 Picture picture = null;
9 for(LirePictureSortable lp : pictureIdsList)
10 {
11 picture = pictureRepository.findOne(lp.getId ());
12 if(picture != null){
13 result.add(createPictureFound(search.getId (), lp, picture));
14 }
15 }
16
17 /* save pictures found */
18 pictureFoundRepository.saveAll(result);
19
20 return result;
21 }
22
23 /**
24 * Creates a new PictureFound entity from LirePictureSortable
25 */
26 private PictureFound createPictureFound(UUID pictureSearch_id , LirePictureSortable lp , Picture
picture)
27 {
28 PictureFound result = new PictureFound ();
29 result.setPictureSearch_id(pictureSearch_id);
30 result.setTotalScore(lp.getScore ());
31 result.setPicture(picture);
32
33 /* set score for each feature */
34 for(Map.Entry <FeatureEnumerate , Float > entry : lp.getDescriptor ().entrySet ()) {
35 switch (entry.getKey ()){
36 case AutoColorCorrelogram:
37 result.setAutocolorCorrelogramScore(entry.getValue ());
38 break;
39 case CEDD:
40 result.setCeddScore(entry.getValue ());
41 break;
42 case ColorLayout:
43 result.setColorLayoutScore(entry.getValue ());
44 break;
45 case EdgeHistogram:
46 result.setEdgeHistogramScore(entry.getValue ());
47 break;
48 case ColorHistogram:
49 result.setColorHistogramScore(entry.getValue ());
50 break;
51 case PHOG:
52 result.setPhogScore(entry.getValue ());
53 break;
54 }
55 }
56
57 return result;
58 }
98 Capıtulo 8
Service Beans sin interfaces
Los servicios se implementan directamente como clases “Service Beans” sin interfa-
ces. Segun la recomendacion de JHipster para la creacion de servicios3, uno de los
principales intereses de Spring es el uso del patron de diseno AOP (Programacion
Orientada a Aspectos). Esta es la tecnologıa que permite que Spring anada compor-
tamientos anadido en sus Beans : por ejemplo, el funcionamiento de transacciones
(como el uso de anotaciones @Transaction).
Con el fin de anadir estos comportamientos, Spring necesita crear un Proxy en
sus clases. Hay dos formas para crear este Proxy:
• Si la clase utiliza una interfaz, Spring va a utilizar un mecanismo estandar de
Java para crear un Proxy dinamico.
• Si la clase no utiliza una interfaz, Spring utilizara la librerıa CGLIB [CGlib, 2005]
para generar una nueva clase sobre la marcha: esto no es un mecanismo
estandar de Java, pero funciona como un mecanismo estandar de Spring.
Una de las principales ventajas de no utilizar interfaces en los servicios es el
rendimiento mediante CGLib para crear en caliente nueva clase Proxy. Ademas,
facilita el uso de nuevos frameworks de Mocking (como EasyMock) para crear muy
pruebas unitarias directamente sin interfaces o sin tener que cambiar el codigo de
produccion para los tests.
8.1.5 Controlador y Servicio Web
Los servicios web RESTful son creados desde la capa controlador de Spring MVC
[Spring, 2015c]. Para servir un servicios del servicio REST se utilizan anotaciones
de clase como @RestController y @RequestMapping(/api”) donde se determina la
ruta de la API.
Las clases de la API se encuentran en los paquetes gal.udc.fic.muei.tfm.dap.flipper.web.rest
en las clases “Resource”. A continuacion se muestra la clase “PictureResource” con
los metodos de llamada a la API.
1 /**
3http://jhipster.github.io/creating a service.html
Implementacion y pruebas 99
2 * REST controller for managing Picture.
3 */
4 @RestController
5 @RequestMapping("/api")
6 public class PictureResource {
7
8 private final Logger log = LoggerFactory.getLogger(PictureResource.class);
9
10 @Inject
11 private PictureRepository pictureRepository;
12
13 @Inject
14 private PictureMapper pictureMapper;
15
16 @Inject
17 private UserRepository userRepository;
18
19 /**
20 * GET /pictures -> get all the pictures.
21 */
22 @RequestMapping(value = "/pictures",
23 method = RequestMethod.GET ,
24 produces = MediaType.APPLICATION_JSON_VALUE)
25 @Timed
26 @Transactional(readOnly = true)
27 public ResponseEntity <List <PictureListDTO >> getAll(@RequestParam(value = "page" ,
required = false) Integer offset ,
28 @RequestParam(value = "per_page", required = false) Integer limit) {
29 log.debug("REST request to get all Pictures");
30 try {
31 Page <Picture > page = pictureRepository.findAllOrdered(PaginationUtil.
generatePageRequest(offset , limit));
32 HttpHeaders headers = headers = PaginationUtil.
generatePaginationHttpHeaders(page , "/api/pictures", offset , limit
);
33 return new ResponseEntity <>(page.getContent ().stream ()
34 .map(pictureMapper :: pictureToPictureListDTO)
35 .collect(Collectors.toCollection(LinkedList ::new)), headers ,
HttpStatus.OK);
36 } catch (URISyntaxException e) {
37 e.printStackTrace ();
38 return ResponseEntity.badRequest ()
39 .header("Failure", e.getMessage ())
40 .body(null);
41 }
42 }
43
44 /**
45 * GET /pictures /:id -> get the "id" picture.
46 */
47 @RequestMapping(value = "/pictures /{id}",
48 method = RequestMethod.GET ,
49 produces = MediaType.APPLICATION_JSON_VALUE)
50 @Timed
51 public ResponseEntity <PictureDTO > get(@PathVariable String id) {
52 log.debug("REST request to get Picture : {}", id);
53 return Optional.ofNullable(pictureRepository.findOne(UUID.fromString(id)))
54 .map(pictureMapper :: pictureToPictureDTO)
55 .map(pictureDTO -> new ResponseEntity <>(
56 pictureDTO ,
57 HttpStatus.OK))
58 .orElse(new ResponseEntity <>( HttpStatus.NOT_FOUND));
59 }
60
61 /**
62 * GET /pictures/byUser /: owner -> get all the pictures from a owner.
63 */
64 @RequestMapping(value = "/pictures/byUser /{ owner}",
100 Capıtulo 8
65 method = RequestMethod.GET ,
66 produces = MediaType.APPLICATION_JSON_VALUE)
67 @Timed
68 @Transactional(readOnly = true)
69 public List <PictureListDTO > getAllFromOwner(@PathVariable String owner) {
70 log.debug("REST request to get all Pictures");
71
72 Optional <User > user = userRepository.findOneByLogin(owner);
73 if(user.isPresent ()){
74 /* get pictures from user */
75 return pictureRepository.findByOwner(user.get().getLogin ()).stream ()
76 .map(picture -> pictureMapper.pictureToPictureListDTO(picture))
77 .collect(Collectors.toCollection(LinkedList ::new));
78 }
79
80 return new ArrayList <>();
81 }
82
83 /*
84 * SEARCH /_search/pictures /: query -> search for the category corresponding
85 * to the query.
86 * Valid values for query: <i>searchText </i> or <i>field:searchText </i>
87 * Valid wildcards for searchText: *, ?, -, !
88 * Valid columns for query:
89 * title text (by default)
90 * description text
91 * created timestamp
92 * owner text
93 * modified timestamp
94 * modifiedby text
95 * autocolorcorrelogram list <double >
96 * autocolorcorrelogramasbase64 text
97 * cedd list <double >
98 * ceddasbase64 text
99 * colorhistogram list <double >
100 * colorhistogramasbase64 text
101 * colorlayout list <double >
102 * colorlayoutasbase64 text
103 * edgehistogram list <double >
104 * edgehistogramasbase64 text
105 * phog list <double >
106 * phogasbase64 text
107 */
108 @RequestMapping(value = "/_search/pictures /{ query}",
109 method = RequestMethod.GET ,
110 produces = MediaType.APPLICATION_JSON_VALUE)
111 @Timed
112 @Transactional(readOnly = true)
113 public List <PictureListDTO > search(@PathVariable String query) {
114 List <PictureListDTO > result = new ArrayList <>();
115
116 if(query.isEmpty ()){
117 query = "*";
118 }
119
120 result.addAll(pictureRepository.search(query).stream ()
121 .map(pictureMapper :: pictureToPictureListDTO)
122 .collect(Collectors.toList ()));
123
124 return result;
125 }
126 }
Dado que la aplicacion esta implementada en Java 8 se aprovecha el uso de lamdas
y los metodos de stream(), map, filter o collect para reducir las lıneas de codigo
Implementacion y pruebas 101
y optimizar las mismas operaciones. En esta clase solo se permiten operaciones
publicas como obtener imagenes, leer una imagen a partir de su identificador o
buscar imagenes por atributos de Cassandra.
Para las operaciones de usuario: envıo de imagenes, edicion y borrado se crea el
acceso a la API /api/mypictures.
1 /**
2 * REST controller for managing Picture.
3 */
4 @RestController
5 @RequestMapping("/api")
6 public class MyPictureResource {
7
8 ...
9
10 /**
11 * POST /mypictures -> Create a new picture.
12 */
13 @RequestMapping(value = "/mypictures",
14 method = RequestMethod.POST ,
15 produces = MediaType.APPLICATION_JSON_VALUE)
16 @Timed
17 public ResponseEntity <PictureDTO > create(@Valid @RequestBody PictureCreateDTO pictureDTO)
throws URISyntaxException {
18 if (pictureDTO.getId () != null) {
19 return ResponseEntity.badRequest ().header("Failure", "A new picture cannot
already have an ID").body(null);
20 }
21 Picture picture = pictureMapper.pictureCreateDTOToPicture(pictureDTO);
22
23 // If fails read metadata , response with warnings in the header
24 try {
25 /* create picture and metadata */
26 Picture result = pictureService.create(picture);
27 return ResponseEntity.created(new URI("/api/mypictures/" + result.getId ()))
28 .headers(HeaderUtil.createEntityCreationAlert("picture", result.getId ().
toString ()))
29 .body(pictureMapper.pictureToPictureDTO(result));
30 } catch (ImageProcessingException e) {
31 e.printStackTrace ();
32 return ResponseEntity.badRequest ().header("Warning", "Cannot process image from
picture: "+e.getMessage ()).body(null);
33 } catch (IOException e) {
34 e.printStackTrace ();
35 return ResponseEntity.badRequest ().header("Warning", "Cannot create feature
index from picture: "+e.getMessage ()).body(null);
36 }
37
38 }
39
40 /**
41 * PUT /mypictures -> Updates an existing picture.
42 */
43 @RequestMapping(value = "/mypictures",
44 method = RequestMethod.PUT ,
45 produces = MediaType.APPLICATION_JSON_VALUE)
46 @Timed
47 public ResponseEntity <PictureDTO > update(@Valid @RequestBody PictureUpdateDTO pictureDTO)
throws URISyntaxException {
48 if (pictureDTO.getId() == null) {
49 return ResponseEntity.badRequest ()
50 .header("Failure", "Picture must have an ID")
51 .body(pictureMapper.pictureToPictureDTO(null));
102 Capıtulo 8
52 }
53
54 Picture picture = pictureMapper.pictureUpdateDTOToPicture(pictureDTO);
55 String owner = pictureRepository.findOne(picture.getId ()).getOwner ();
56
57 /* check if it is owner */
58 String currentUser = SecurityUtils.getCurrentLogin ();
59 if( !owner.equalsIgnoreCase(currentUser) ){
60 return ResponseEntity.badRequest ()
61 .header("Failure", "You are not the owner from this picture.")
62 .body(pictureMapper.pictureToPictureDTO(null));
63 }
64
65 Picture result = pictureService.update(picture);
66 return ResponseEntity.ok()
67 .headers(HeaderUtil.createEntityUpdateAlert("picture", pictureDTO.getId ().toString ()))
68 .body(pictureMapper.pictureToPictureDTO(result));
69 }
70
71 /**
72 * GET /mypictures -> get all the pictures from current user.
73 */
74 @RequestMapping(value = "/mypictures",
75 method = RequestMethod.GET ,
76 produces = MediaType.APPLICATION_JSON_VALUE)
77 @Timed
78 @Transactional(readOnly = true)
79 public List <PictureListDTO > getAll () {
80 return pictureRepository.findByOwner(SecurityUtils.getCurrentLogin ()).stream ()
81 .map(picture -> pictureMapper.pictureToPictureListDTO(picture))
82 .collect(Collectors.toCollection(LinkedList ::new));
83 }
84
85 /**
86 * DELETE /mypictures /:id -> delete the "id" picture.
87 */
88 @RequestMapping(value = "/mypictures /{id}",
89 method = RequestMethod.DELETE ,
90 produces = MediaType.APPLICATION_JSON_VALUE)
91 @Timed
92 public ResponseEntity <Void > delete(@PathVariable String id) {
93 try {
94 Picture picture = pictureRepository.findOne(UUID.fromString(id));
95 if( !picture.getOwner ().equalsIgnoreCase(SecurityUtils.getCurrentLogin ()) ){
96 return ResponseEntity.badRequest ()
97 .header("Failure", "You are not the owner from this picture.")
98 .body(null);
99 }
100 pictureService.delete(UUID.fromString(id));
101
102 } catch (IOException e) {
103 e.printStackTrace ();
104 return ResponseEntity.badRequest ().header("Warning", "Cannot delete feature index from
picture: " + e.getMessage ()).body(null);
105 }
106 return ResponseEntity.ok().headers(HeaderUtil.createEntityDeletionAlert("picture", id.
toString ())).build ();
107 }
108 }
En “MyPictureRepository” los metodos realizan la comprobacion de acceso del
usuario para comprobar si tiene acceso. Para enviar las imagenes se realizar a traves
del objeto “PictureCreateDTO”, el cual debe incluir el binario de la imagen. Los
datos recibidos de entrada son en formato JSON y las imagenes en formato binario
Implementacion y pruebas 103
en Base64.
DTOs y Mappers
Los objetos DTO (Data Transfer Object o Objeto de Transferencia de Datos) sirven
para almacenar los datos de entrada y salida de la API, son objetos simples que no
continen logica de negocio. Ademas, evitan mostrar todos los datos de la entidad
y aumento en el coste de llamadas a las entidades. Para mapear los DTO con las
entidades se usan las interfaces de MapStruct [Morling, 2005]. A continuacion se
muestran un ejemplo de Mapper para la entidad “Pictures”.
1 /**
2 * Mapper for the entity Picture and its DTO PictureDTO.
3 */
4 @Mapper(componentModel = "spring", uses = {ByteMapper.class , DateMapper.class })
5 public interface PictureMapper {
6
7 PictureDTO pictureToPictureDTO(Picture picture);
8
9 Picture pictureCreateDTOToPicture(PictureCreateDTO pictureCreateDTO);
10
11 Picture pictureUpdateDTOToPicture(PictureUpdateDTO pictureDTO);
12
13 PictureListDTO pictureToPictureListDTO(Picture picture);
14 }
15
16 /**
17 * Mapper for ByteBuffer type for Cassandra entities
18 */
19 @Mapper(componentModel = "spring")
20 public class ByteMapper {
21 public byte[] asByte(ByteBuffer pictureFile) {
22 return pictureFile != null ? pictureFile.array() : null;
23 }
24
25 public ByteBuffer asByteBuffer(byte[] pictureFile) {
26 return pictureFile != null ? ByteBuffer.wrap(pictureFile) : null;
27 }
28 }
29
30 /**
31 * Mapper for timestamp type date
32 */
33 @Mapper(componentModel = "spring")
34 public class DateMapper {
35
36 public String asString(Date date) {
37 return date != null ? new SimpleDateFormat( "dd/MM/yyyy" )
38 .format(date ) : null;
39 }
40
41 public Date asDate(String date) {
42 try {
43 return date != null ? new SimpleDateFormat( "dd/MM/yyyy" )
44 .parse( date ) : null;
45 }
46 catch ( ParseException e ) {
47 throw new RuntimeException( e );
48 }
104 Capıtulo 8
49 }
50 }
“ByteMapper” y “DateMapper” son clases utilizados en las interfaces Mapper
de las entidades para traducir los datos de entrada de los DTOs hacia los datos de
las entidades. Al crear o realizar cambios en una clase DTO, se recomienda compilar
con Maven para generar los mappers de MapStruct.
$ mvn compile
Para evitar mensajes de error de los atributos de las entidades con la anotacion
@IgnoreJson (aquellos que no se desea mostrar en el servicio REST), se recomienda
anadir la siguiente configuracion al proyecto de Maven:
1 <dependencies >
2 <dependency >
3 <groupId >org.mapstruct </groupId >
4 <artifactId >mapstruct -processor </artifactId >
5 <version >${ mapstruct.version}</version >
6 </dependency >
7 </dependencies >
8 <configuration >
9 <defaultOutputDirectory >${ project.build.directory }/generated -sources </
defaultOutputDirectory >
10 <processors >
11 <processor >org.mapstruct.ap.MappingProcessor </processor >
12 </processors >
13 <options >
14 <mapstruct.suppressGeneratorTimestamp >true</mapstruct.
suppressGeneratorTimestamp >
15 <mapstruct.defaultComponentModel >spring </mapstruct.
defaultComponentModel >
16 <!-- Ignore unmapped fields from Entities -->
17 <mapstruct.unmappedTargetPolicy >IGNORE </mapstruct.unmappedTargetPolicy >
18 </options >
19 </configuration >
Para la busqueda de imagenes, se crean dos metodos para la busqueda en “Pic-
tureSearchResource”:
1 /**
2 * SEARCH /_search/pictureSearchs/ -> search for similar pictures
3 * Upload a json picture search pictureFile as Base64
4 * @return
5 */
6 @RequestMapping(
7 value="/_search/pictureSearchs",
8 method=RequestMethod.POST ,
9 produces = MediaType.APPLICATION_JSON_VALUE)
10 @Timed
11 public @ResponseBody ResponseEntity <PictureSearchDTO > search(@Valid @RequestBody
12 PictureSearchCreateDTO pictureSearchCreateDTO) throws URISyntaxException {
13 log.debug("REST request to search Pictures : {}", pictureSearchCreateDTO);
14
15 try {
16 PictureSearch result = pictureSearchService.searchByAllFeatures(
pictureSearchCreateDTO.getPictureFile ());
Implementacion y pruebas 105
17 return ResponseEntity.created(new URI("/api/pictureFound/" + result.getId()
))
18 .headers(HeaderUtil.createEntityCreationAlert("pictureSearch", result.getId
().toString ()))
19 .body(pictureSearchMapper.pictureSearchToPictureSearchDTO(result));
20 } catch (IOException e) {
21 e.printStackTrace ();
22 return ResponseEntity.badRequest ().header("Warning", "Cannot search from
picture: " + e.getMessage ()).body(null);
23 }
24 }
El primer metodo, recibe un DTO PictureSearchCreateDTO con unicamente una
imagen en formato texto Base64, esto es necesario para poder ser implementada la
busqueda en AngularJs debido a un problema de compatibilidad de la librerıa de
Angular-Resource con los tipos de datos file. El valor devuelto por el metodo POST
es el objeto creado, que luego sera utilizado por la interfaz para redirigir hacia las
imagenes encontradas en el listado de “PictureFound”.
Otro metodo es implementado para facilitar las operaciones de busqueda direc-
tamente sobre la API REST, recibe como entrada una imagen como objeto “Multi-
partFile” y devuelve directamente todas las imagenes encontradas.
1 /**
2 * SEARCH /_search/pictureSearch/byFile/ -> search for similar pictures by input file
3 * Upload a picture file as input file type
4 * @return
5 */
6 @RequestMapping(
7 value="/_search/pictureSearch/byFile",
8 method=RequestMethod.POST ,
9 produces = MediaType.APPLICATION_JSON_VALUE)
10 @Timed
11 public @ResponseBody
12 List <PictureFoundDTO > search(@RequestParam("file") MultipartFile file) throws
URISyntaxException {
13
14 try {
15 return pictureSearchService.searchByAllFeaturesWithList(file.getBytes ()
).stream ()
16 .map(pictureFound -> pictureFoundMapper.
pictureFoundToPictureFoundDTO(pictureFound))
17 .collect(Collectors.toCollection(LinkedList ::new));
18
19 } catch (IOException e) {
20 e.printStackTrace ();
21 return new ArrayList <>();
22 }
23
24 }
106 Capıtulo 8
8.1.6 Aplicacion Lado Cliente o Front-End
La interfaz web creada para comunicarse con el servicio web esta escrita en Angu-
larJS, en la ruta src/main/webapp/scripts se encuentran los recursos de la aplicacion:
vistas HTML y ficheros js de los controladores, modelos de la vista y servicios de
AngularJs.
Listado de Imagenes con Paginacion
Las imagenes mostradas se obtienen ordenadas por fecha descendente con pagina-
cion. En el controlador de lista de imagenes se realizan las operaciones de obtener
listas de imagenes.
1 ’use strict ’;
2
3 $scope.pictures = [];
4 $scope.page = 1;
5 $scope.loadAll = function () {
6 /** Infinity Scroll */
7
8 Picture.query ({page: $scope.page , per_page: 20}, function(result , headers) {
9 $scope.links = ParseLinks.parse(headers(’link’));
10 for (var i = 0; i < result.length; i++) {
11 $scope.pictures.push(result[i]);
12 }
13 });
14 };
15 $scope.reset = function () {
16 $scope.page = 1;
17 $scope.pictures = [];
18 $scope.loadAll ();
19 };
20 $scope.loadPage = function(page) {
21 $scope.page = page;
22 $scope.loadAll ();
23 };
24 $scope.loadAll ();
25
26 $scope.search = function () {
27 if($scope.searchQuery != ""){
28 PictureSearchQuery.query ({ query: $scope.searchQuery}, function(result)
{
29 $scope.pictures = result;
30 }, function(response) {
31 if(response.status === 404) {
32 $scope.loadAll ();
33 }
34 });
35 }
36 };
37 $scope.refresh = function () {
38 $scope.loadAll ();
39 $scope.clear ();
40 };
41
42 $scope.clear = function () {
43 $scope.picture = {title: null , description: null , pictureFile: null , owner: null ,
modifiedBy: null , favourites: null , likes: null , created: null , modified: null , id
Implementacion y pruebas 107
: null};
44 };
45 });
La vista utiliza el plugin InfiniteScroll para obtener los siguientes elementos y
opcionalmente la paginacion normal de imagenes.
1 <div>
2 <div class="row">
3 <div class="col -lg -12">
4 <h1 class="page -header" translate="flipperApp.picture.home.title">Pictures
5 <small >best pictures list from Flipper </small >
6 </h1>
7 </div>
8 </div>
9
10 <div class="container">
11 <div class="row">
12 <div class="col -md -12">
13 <form name="searchForm" class="form -inline">
14 <div class="form -group">
15 <input type="text" class="form -control" ng -model="searchQuery" id="searchQuery"
placeholder="search picture" required="true"/>
16 </div>
17 <button class="btn btn -info" ng -click="search ()"><span class="glyphicon
glyphicon -search"></span> <span>Search </span>
18 </button >
19 </form>
20 </div>
21 </div>
22 </div>
23
24 <div class="container top25">
25 <div class="row" infinite -scroll="loadPage(page + 1)" infinite -scroll -disabled="links[’last
’] == page">
26 <div ng -repeat="picture in pictures">
27 <div ng -if="$index % 4 == 0" class="clearfix"></div>
28 <div class="col -xs -4 col -sm -3 col -md -3 portfolio -item">
29 <a ui -sref="picture.detail ({id:picture.id})">
30 <img data -ng -src="{{’data:image /*;base64 ,’ + picture.
littlePictureFile }}"
31 alt="{{ picture.title}}" class="img -thumbnail" ng -if="picture.
littlePictureFile"/>
32 </a>
33 <h3><a ui -sref="picture.detail ({id:picture.id})">{{ picture.title}}<
/a></h3>
34 <p>{{ picture.owner}} {{ picture.created }}</p>
35 <p>{{ picture.description }}</p>
36 </div>
37 </div>
38 </div>
39 </div>
Busqueda de Imagenes por Contenido
En src/main/webapp/scripts/app/entities/pictureSearch/pictureSearch-dialog.controller.js
esta el controlador encargado de enviar las imagenes para las busquedas por conte-
nido.
1 ’use strict ’;
108 Capıtulo 8
2
3 angular.module(’flipperApp ’).controller(’PictureSearchDialogController ’,
4 [’$scope ’, ’$stateParams ’, ’$modalInstance ’, ’$location ’, ’entity ’, ’PictureSearch ’, ’
PictureSearchByPicture ’,
5 function($scope , $stateParams , $modalInstance , $location , entity , PictureSearch ,
PictureSearchByPicture) {
6
7 $scope.pictureSearch = entity;
8
9 var onSaveFinished = function (result) {
10 /* not emit */
11 // $scope.$emit(’flipperApp:pictureSearchUpdate ’, result);
12 $modalInstance.close(result);
13 };
14
15 $scope.save = function () {
16 PictureSearchByPicture.save($scope.pictureSearch , onSaveFinished);
17 };
18
19 $scope.byteSize = function (base64String) {
20 if (! angular.isString(base64String)) {
21 return ’’;
22 }
23 function endsWith(suffix , str) {
24 return str.indexOf(suffix , str.length - suffix.length) !== -1;
25 }
26 function paddingSize(base64String) {
27 if (endsWith(’==’, base64String)) {
28 return 2;
29 }
30 if (endsWith(’=’, base64String)) {
31 return 1;
32 }
33 return 0;
34 }
35 function size(base64String) {
36 return base64String.length / 4 * 3 - paddingSize(base64String);
37 }
38 function formatAsBytes(size) {
39 return size.toString ().replace (/\B(?=(\d{3}) +(?!\d))/g, " ") + " bytes";
40 }
41
42 return formatAsBytes(size(base64String));
43 };
44 $scope.setPictureSearchFile = function ($files , pictureSearch) {
45
46 if ($files [0]) {
47 var file = $files [0];
48 var fileReader = new FileReader ();
49 fileReader.readAsDataURL(file);
50 fileReader.onload = function (e) {
51 var data = e.target.result;
52 var base64Data = data.substr(data.indexOf(’base64 ,’) + ’base64 ,’.length);
53 $scope.$apply(function () {
54 pictureSearch.pictureFile = base64Data;
55 });
56 };
57 }
58 };
59
60 $scope.clear = function () {
61 $modalInstance.dismiss(’cancel ’);
62 };
63 }]);
Implementacion y pruebas 109
El metodo setPictureSearchFile recibe la imagen del campo de entrada y con-
vierte el valor binario en Base64 en un campo de texto oculto para enviarlo como
parte de los campos de entrada a la API Rest.
El servicio para las busquedas de imagenes se encuentra en la ruta de com-
ponentes src/main/webapp/scripts/components/, en entities/pictureSearch/picture-
Search.service.js. Se define los paramentros de entrada para la busqueda con la
operacion GET y las consultas de busqueda por imagen como POST.
1 ’use strict ’;
2
3 angular.module(’flipperApp ’)
4 .factory(’PictureSearch ’, function ($resource , DateUtils) {
5 return $resource(’api/pictureSearchs /:id’, {}, {
6 ’query ’: { method: ’GET’, isArray: true},
7 ’get’: {
8 method: ’GET’,
9 transformResponse: function (data) {
10 data = angular.fromJson(data);
11 return data;
12 }
13 }
14 });
15 })
16 .factory(’PictureSearchByPicture ’, function ($resource , DateUtils) {
17 return $resource(’api/_search/pictureSearchs ’, {}, {
18 ’save’: { method: ’POST’}
19 });
20 });
8.2 PRUEBAS
8.2.1 Pruebas Unitarias
El entorno de pruebas unitarias se realiza con la biblioteca de JUnit.
Las clases test de Java se encuentran en la ruta src/test/java/gal/udc/fic/-
muei/tfm/dap/flipper/
• model/MetadataTest.java
• model/PictureTest.java
• model/UserTest.java
• web/rest/AccountResourceTest.java
• web/rest/MetadataResourceTest.java
110 Capıtulo 8
• web/rest/PictureFoundResourceTest.java
• web/rest/PictureResourceTest.java
• web/rest/PictureSearchResourceTest.java
• web/rest/UserResourceTest.java
8.2.2 Pruebas de Integracion
Las pruebas realizadas unitarias con JUnit sirven para comprobar el exito de acciones
sobre el modelo y controladores, creando a partir de registros de las tablas del
proyecto sobre la base de datos test.
Las pruebas de integracion se realizan en el ambito de la aplicacion una vez que
se han aprobado las pruebas unitarias. Consisten en verificar que un conjunto de
partes de la aplicacion y acciones funcionan perfectamente.
Durante el proceso de creacion del proyecto se han ido realizado pruebas de
las acciones sobre las vistas creadas para comprobar que el conjunto Modelo-Vista-
Controlador funcionaban correctamente. Las pruebas de los servicios web se generan
usando el framework de Mockito4. Mockito es un framework para escribir pruebas
unitarias faciles de leer con creacion y verificacion de datos. En el arranque de cada
test se inician los datos con Mockito y se crean las instancias de las entidades. En el
siguiente codigo se muestra un ejemplo del test de la API REST de Metadata. Las
clases “MockMvc” y “MockMvcBuilders” permiten definir las clases “Repository” y
realizar la conversion de los datos desde la capa controlador.
1 @PostConstruct
2 public void setup() {
3 MockitoAnnotations.initMocks(this);
4 MetadataResource metadataResource = new MetadataResource ();
5 ReflectionTestUtils.setField(metadataResource , "metadataRepository", metadataRepository);
6 ReflectionTestUtils.setField(metadataResource , "metadataMapper", metadataMapper);
7 this.restMetadataMockMvc = MockMvcBuilders.standaloneSetup(metadataResource).setMessageConverters(
jacksonMessageConverter).build ();
8 }
9
10 @Before
11 public void initTest () {
12 metadataRepository.deleteAll ();
13 metadata = new Metadata ();
14 metadata.setDirectoryName(DEFAULT_DIRECTORY_NAME);
15 metadata.setTagType(DEFAULT_TAG_TYPE);
16 metadata.setTagName(DEFAULT_TAG_NAME);
4http://mockito.org/
Implementacion y pruebas 111
17 metadata.setDescription(DEFAULT_DESCRIPTION);
18 metadata.setPicture_id(DEFAULT_PICTURE_ID);
19 metadata.setTitle(DEFAULT_TITLE);
20 metadata.setPictureFile(ByteBuffer.wrap(DEFAULT_PICTURE_FILE));
21 }
Para crear una camada al Mock como si fuese un servicio REST real, tan solo
es necesario crear el objeto DTO y enviarlo a la ruta definida en la aplicacion.
1
2 // Create the Metadata
3 MetadataDTO metadataDTO = metadataMapper.metadataToMetadataDTO(metadata);
4
5 // Call API REST for metadatas list
6 restMetadataMockMvc.perform(post("/api/metadatas")
7 .contentType(TestUtil.APPLICATION_JSON_UTF8)
8 .content(TestUtil.convertObjectToJsonBytes(metadataDTO)))
9 .andExpect(status ().isCreated ());
Las pruebas del front-end se encuentran la librerıa KarmaJs con implementacion
de prueba del servicio de usuario en la ruta src/test/javascript/.
A continuacion se realizan las pruebas de verificacion con los navegadores Firefox5 y Google Chrome 6. En ambos casos se han utilizado las herramientas de depuracion
para desarrolladores incluidas por defecto.
8.2.3 Pruebas de Estres
Las pruebas de estres de Cassandra se realizan con la herramienta de pruebas de
estres “cassandra-stress”. Las pruebas de estres se pueden definir en ficheros de
configuracion YAML7 siguiendo las recomendaciones de mejoras para Cassandra 2.1
de DataStax [Luciani, 2014]. A continuacion se muestra el fichero de YAML para
las pruebas de estres de la tabla “Picture”.
1 #
2 # Keyspace info
3 #
4 keyspace: flippertest
5
6 #
7 # The CQL for creating a keyspace (optional if it already exists)
8 #
9 keyspace_definition: |
10 CREATE KEYSPACE flippertest WITH replication = {’class ’: ’SimpleStrategy ’, ’replication_factor ’ : 1 }
11
12 #
13 # Table info
14 #
5http://www.mozilla.org/es-ES/firefox/6http://www.google.com/intl/es/chrome/7http://www.yaml.org/start.html
112 Capıtulo 8
15 table: picture
16
17 #
18 # The CQL for creating a table you wish to stress (optional if it already exists)
19 #
20 table_definition: |
21 CREATE TABLE picture (
22 id uuid ,
23 title text ,
24 description text ,
25 pictureFile blob ,
26 littlePictureFile blob ,
27 mediumPictureFile blob ,
28 bigPictureFile blob ,
29 created timestamp ,
30 modified timestamp ,
31 owner text ,
32 modifiedBy text ,
33 autocolorCorrelogramAsBase64 text ,
34 autocolorCorrelogram list<double >,
35 ceddAsBase64 text ,
36 cedd list<double >,
37 colorHistogramAsBase64 text ,
38 colorHistogram list<double >,
39 colorLayoutAsBase64 text ,
40 colorLayout list<double >,
41 edgeHistogramAsBase64 text ,
42 edgeHistogram list<double >,
43 phogAsBase64 text ,
44 phog list<double >,
45 PRIMARY KEY(id)
46 )
47 insert:
48 partitions: uniform (1..50) # number of unique partitions to update in a single operation
49 # if batchcount > 1, multiple batches will be used but all partitions will
50 # occur in all batches (unless they finish early); only the row counts will vary
51 batchtype: LOGGED # type of batch to use
52 select: uniform (1..10) /10 # uniform chance any single generated CQL row will be visited in a partition;
53 # generated for each partition independently , each time we visit it
54
55 #
56 # A list of queries you wish to run against the schema
57 #
58 queries:
59 singlepictuce:
60 cql: SELECT * from picture WHERE id = ? LIMIT 1
61 fields: samerow
62 owner:
63 cql: SELECT * from picture WHERE id IN (?) LIMIT 100
64 fields: samerow # samerow or multirow (select arguments from the same row , or
randomly from all rows in the partition)
65 wherein:
66 cql: select * from picture where owner = ? LIMIT 100 # Needs create index on picture (
owner); manually
67 fields: samerow
Para lanzar las pruebas de estres en el cluster:
#!/bin/bash
# Running Mixed picture test on flippertest keyspace
cassandra -stress user profile =./ flipper -picture.yaml ops\( single=1,onwer=1,
insert =1\)
Implementacion y pruebas 113
# Running other tables
cassandra -stress user profile =./ flipper -metadata.yaml ops\( single=1,picture
=1,insert =1\)
cassandra -stress user profile =./ flipper -pictureFound.yaml ops\(
picturesearch =1,owner=1,insert =1\)
cassandra -stress user profile =./ flipper -pictureSearch.yaml ops\( single=1,
owner=1,insert =1\)
cassandra -stress user profile =./ flipper -user.yaml ops\( single=1,login1 ,
insert =1\)
El tiempo de duracion de las pruebas depende del equipo y las operaciones a
realizar, 50000 operaciones en las pruebas por defecto. Para las pruebas de estres
realizadas en el modo de desarrollo se utiliza un equipo Intel Dual Core con 8Gb de
RAM y disco SSD Sata 2 modelo Kingstom.
A continuacion se muestran los resultados de insercion y lectura sobre tabla
“Picture”.
# Inserts
Running with 4 threadCount
Running [insert] with 4 threads until stderr of mean < 0.02
Results:
op rate : 47 [insert :47]
partition rate : 1191 [insert :1191]
row rate : 1191 [insert :1191]
latency mean : 83,8 [insert :83,8]
latency median : 65,5 [insert :65,5]
latency 95th percentile : 233,8 [insert :233 ,8]
latency 99th percentile : 322,2 [insert :322 ,2]
latency 99.9th percentile : 428,6 [insert :428 ,6]
latency max : 614,4 [insert :614 ,4]
Total partitions : 154555 [insert :154555]
Total errors : 0 [insert :0]
total gc count : 179
total gc mb : 24880
total gc time (s) : 19
avg gc time(ms) : 105
stdev gc time(ms) : 31
Total operation time : 00:02:09
# Reads
Running with 4 threadCount
Running [insert] with 4 threads until stderr of mean < 0.02
Results:
op rate : 383 [singlepicture :383]
partition rate : 312 [singlepicture :312]
row rate : 312 [singlepicture :312]
114 Capıtulo 8
latency mean : 10,4 [singlepicture :10,4]
latency median : 4,6 [singlepicture :4,6]
latency 95th percentile : 10,4 [singlepicture :10 ,4]
latency 99th percentile : 21,4 [singlepicture :21 ,4]
latency 99.9th percentile : 1456 ,7 [singlepicture :1456 ,7]
latency max : 2712,7 [singlepicture :2712 ,7]
Total partitions : 75998 [singlepicture :75998]
Total errors : 0 [singlepicture :0]
total gc count : 254
total gc mb : 36805
total gc time (s) : 31
avg gc time(ms) : 123
stdev gc time(ms) : 60
Total operation time : 00:04:03
Sleeping for 15s
Para una prueba de estres con 4 hilos se realizan unas 47 inserciones y 383
lecturas por segundo, en un unico de pruebas con las especificaciones de hardware
descritas.
8.2.4 Pruebas de Compatibilidad
La aplicacion es funcional los navegadores Firefox, Iceweasel y Google Chrome. En
algunos casos se pueden apreciar pequenos cambios en la forma de representar obje-
tos HTML, segun las opciones de las hojas de estilos pero en ningun caso afectando
a la funcionalidad y rendimiento de la aplicacion.
8.2.5 Pruebas de Aceptacion
El objetivo de las pruebas de aceptacion es validar que un sistema cumple con
el funcionamiento esperado y permitir al usuario de dicho sistema que determine
su aceptacion, desde el punto de vista de su funcionalidad y rendimiento. En esta
aplicacion las pruebas de aceptacion han sido realizadas por el director.
Para la carga de imagenes se utilizan una serie de muestras personales del alumno
junto con imagenes extraıdas de la API de la red social de Twitter, con diferentes
temas de coches, videojuegos, conferencias, paisajes y textos escritos. Para realizar
la carga se utiliza la herramienta en lınea de comandos “curl” llamando al recurso
de carga de imagenes del usuario en el servicio REST /api/mypictures/upload.
La autenticacion de usuario se realiza mediante la llamada al recurso /api/aut-
henticate. Esta llamada al sistema realiza la operacion de
Implementacion y pruebas 115
$ cd scripts_sample
$ cat login.sh
#!/ bin/bash
if [ $# -ne 2 ];
then
echo "usage: $0 username password ";
exit -1;
fi;
USER=$1
PASS=$2
curl -X POST http :// localhost :8080/ api/authenticate -H "Accept: application
/json" -d "username=$USER&password=$PASS"
$ cat picture_multi_upload.sh
#!/ bin/bash
if [ $# -ne 2 ];
then
echo "usage: $0 token path_images"
exit -1;
fi
TOKEN=$1
touch /tmp/run.sh;
for i in ‘ls $2*.jpg ‘;
do
fname=‘basename $i‘
fbname=${fname %.jpg}
echo "curl -i -v -X POST -F \" pictureFile=@$i\" -F \" title=$fbname
\" -F \" description=description from $fbname \" http :// localhost
:8080/ api/mypictures/upload -H \"x-auth -token:$TOKEN \" -H \"
Accept:application/json \";" >> /tmp/run.sh;
done
bash /tmp/run.sh > /dev/null 2>&1;
rm /tmp/run.sh;
116 Capıtulo 8
8.3 HERRAMIENTAS UTILIZADAS
8.3.1 Cassandra Community Edition
Para la creacion de la estructura de tablas en Cassandra se ha utilizado la anotacion
de sintaxis CQL y el interprete para lınea de comandos cqlsh que incluye el paquete
basico Cassandra Community Edition.
8.3.2 DataStax Enterprise Edition
Para la configuracion de Cassandra DataStax sobre un cluster de Hadoop [Cutting, 2015b]
y el uso de Solr [Cutting, 2015a] como buscador de datos con el paquete DSE Search.
Mediante lınea de comandos se puede configurar de forma automatica el mapeo de las
tablas de Cassandra en Solr, equivale a crear manualmente los ficheros schema.xml
y solrconfig.xml de Solr.
sudo dsetool create_core flipper.picture generateResources=true reindex=true
coreOptions=config -picture.yaml
sudo dsetool create_core flipper.metadata generateResources=true reindex=true
coreOptions=config -metadata.yaml
sudo dsetool create_core flipper.picturesearch generateResources=true reindex=true
coreOptions=config -pictureSearch.yaml
sudo dsetool create_core flipper.picturefound generateResources=true reindex=true
coreOptions=config -pictureFound.yaml
8.3.3 Draw.io
Para crear los diagramas de arquitectura se utiliza la herramienta de diagrama online
Draw.io [JGraph, 2005] de JGraph8.
8.3.4 UML
Para la creacion de los diagramas de clases y de secuencia se utiliza la herramienta
de edicion UML Magic Draw9. Para la creacion de los casos de uso se utiliza la
herramienta online de ”UML Sketching“ Yuml.Me10.
8https://www.jgraph.com/9http://www.nomagic.com/products/magicdraw.html
10http://yuml.me/
Implementacion y pruebas 117
8.3.5 ProjectLibre
ProjectLibre es una aplicacion para la gestion de proyectos. Es una alternativa a
“Microsoft Project” con licencia software libre. Se ejecuta bajo la plataforma Java,
lo que permite que se ejecute en multiples plataformas como GNU/Linux, Mac OS
o Windows. Se distribuye bajo la licencia Common Public Attribution License.
ProjectLibre es un fork de OpenProj publicado en 2012.
Se utiliza para la planificacion del proyecto junto con la definicion de las itera-
ciones mas representativas del proyecto en hitos y su visualizacion en diagramas de
Gantt.
8.3.6 IntelliJ Idea
Para el desarrollo de la aplicacion se ha utilizado el IDE IntelliJ Idea [Brains, 2015]
con la licencia gratuita para estudiantes. Se recomienda anadir la ruta JDK de Java
8 y los plugins de AngularJs y Spring, configurar las clases de configuracion “Java
Bean” de Spring Boot y las dependencias JavaScript de Bower.
8.3.7 Git
Se utiliza Git como gestor de repositorios en el sitio Bitbucket11 para mantener los
cambios de versiones de la aplicacion.
11https://bitbucket.org/
118 Capıtulo 8
9SOLUCION DESARROLLADA
9.1 INTRODUCCION
En este capıtulo se muestran las caracterısticas principales de la aplicacion. A con-
tinuacion se explica como es la interfaz general de la aplicacion. En las diferentes
secciones se incluyen capturas de pantalla de los distintos apartados de la aplicacion
y la descripcion de las funcionalidades principales de la misma.
9.2 API
Para el acceso a los recursos de la aplicacion (inicio de sesion, imagenes, metadatos
y busquedas) es necesario realizar un registro previo y utilizar el token de sesion
asignado a cada peticion de usuario. Este token tiene asociado un tiempo lımite de
espera. A continuacion se muestran ejemplos de llamadas a la API a traves de la
herramienta de lınea de comandos “curl”.
119
120 Capıtulo 9
• Inicio de sesion:
$ curl -i -X POST http :// localhost :8080/ api/
authenticate -H "Accept: application/json" -d "
username=$USER&password=$PASS"
{" token ":" user :1441616523254:79
c4662df5ba4ce8c7ed4ad4d228a2cf ","expires
":1441616523254}
• Listado de imagenes:
$ curl -i http :// localhost :8080/ api/pictures -H "x-
auth -token:user :1441616523254:79
c4662df5ba4ce8c7ed4ad4d228a2cf" -H "Accept:
application/json"
HTTP /1.1 200 OK
Server: Apache -Coyote /1.1
X-Content -Type -Options: nosniff
X-XSS -Protection: 1; mode=block
Cache -Control: no -cache , no-store , max -age=0, must -
revalidate
Pragma: no -cache
Expires: 0
X-Application -Context: application:dev :8080
X-Total -Count: 20
Link: </api/pictures?page =1& per_page =20>; rel="last
",</api/pictures?page =1& per_page =20>; rel="first"
Content -Type: application/json;charset=UTF -8
Transfer -Encoding: chunked
Date: Mon , 07 Sep 2015 10:50:14 GMT
[{"id":" f0cd84fc -65db -4b6e -9216 - c6220b59949d ","title
":" IMG_20150906_132434 "," description ":" description
from IMG_20150906_132434 ","owner ":" user","
littlePictureFile ":"0 x123114123 "//2Q=="," created
":"07/09/2015"}]
• Subida de imagen:
curl -i -X POST -F "pictureFile=@twitter.png" -F "
title=Logo Twitter" -F "description=twitter+logo+
png" http :// localhost :8080/ api/mypictures/upload -
H "x-auth -token:user :1441616523254:79
c4662df5ba4ce8c7ed4ad4d228a2cf" -H "Accept:
application/json"
HTTP /1.1 201 Created
Solucion desarrollada 121
Server: Apache -Coyote /1.1
X-Content -Type -Options: nosniff
X-XSS -Protection: 1; mode=block
Cache -Control: no-cache , no-store , max -age=0, must -
revalidate
Pragma: no-cache
Expires: 0
X-Application -Context: application:dev :8080
Location: /api/mypictures /11909fa2 -f7d7 -4434 -8894 -966
a4714bae8
X-flipperApp -alert: flipperApp.picture.created
X-flipperApp -params: 11909fa2 -f7d7 -4434 -8894 -966
a4714bae8
Content -Type: application/json;charset=UTF -8
Transfer -Encoding: chunked
Date: Mon , 07 Sep 2015 08:43:16 GMT
{"id ":"11909fa2 -f7d7 -4434 -8894 -966 a4714bae8","title
":" Logo Twitter"," description ":" twitter+logo+png
"," pictureFile ":"0 x123 =="," modifiedBy ":null ,"
created ":"07/09/2015" ," modified ":null}
• Busquedas de imagenes por texto:
\begin{lstlisting }[style=console]
curl -i http :// localhost :8080/ api/_search/pictures/
description:logo+png -H "x-auth -token:user
:1441616523254:79 c4662df5ba4ce8c7ed4ad4d228a2cf" -
H "Accept:application/json"
HTTP /1.1 200 OK
Server: Apache -Coyote /1.1
X-Content -Type -Options: nosniff
X-XSS -Protection: 1; mode=block
Cache -Control: no-cache , no-store , max -age=0, must -
revalidate
Pragma: no-cache
Expires: 0
X-Application -Context: application:dev :8080
Content -Type: application/json;charset=UTF -8
Transfer -Encoding: chunked
Date: Mon , 07 Sep 2015 08:44:39 GMT
[{"id ":"11909fa2 -f7d7 -4434 -8894 -966 a4714bae8 ","title
":" Logo Twitter"," description ":" twitter+logo+png
","owner ":" user"," littlePictureFile ": "0x123==", "
created ":"07/09/2015"}]
• Recuperacion de imagenes por contenido:
122 Capıtulo 9
$ curl -i -X POST -F "file=@twitter.png" http ://
localhost :8080/ api/_search/pictureSearch/byFile/
-H "x-auth -token:user :1441616523254:79
c4662df5ba4ce8c7ed4ad4d228a2cf" -H "Accept:
application/json"
HTTP /1.1 200 OK
Server: Apache -Coyote /1.1
X-Content -Type -Options: nosniff
X-XSS -Protection: 1; mode=block
Cache -Control: no -cache , no-store , max -age=0, must -
revalidate
Pragma: no -cache
Expires: 0
X-Application -Context: application:dev :8080
Content -Type: application/json;charset=UTF -8
Transfer -Encoding: chunked
Date: Mon , 07 Sep 2015 08:45:51 GMT
[{"id ":"11909fa2 -f7d7 -4434 -8894 -966 a4714bae8","title
":" Logo Twitter"," description ":" twitter+logo+png
","owner ":" user"," littlePictureFile ": "0x123==", "
created ":"07/09/2015"}]
Para obviar el contenido de la imagen en Base64, se sustituye por la cadena
“0x123==”.
A continuacion se muestra la documentacion con los recursos accesibles para la
API.
9.2.1 Recursos del Usuario
En las figuras 9.1 y 9.2 se muestran los recursos de solicitud de token de acceso y
recursos del propio usuario.
9.2.2 Recursos de Imagenes
En la figura 9.3 se describe la API para los recursos de imagenes. En la figura 9.4,
los recursos de imagenes de los usuarios, para almacenar, lista, editar y borrar su
catalogo de imagenes.
9.2.3 Recursos de Metadatos
En la figura 9.5 se muestran los recursos de los metadatos.
Solucion desarrollada 123
Figura 9.1: API - Creacion de token de accesso.
9.2.4 Recursos de Busquedas por contenido
En las figuras 9.6 y 9.7 se muestran los recursos de busqueda y resultados de busque-
da.
9.3 INTERFAZ DE LA APLICACION
9.3.1 Pagina de inicio
En la figura 9.8 se muestra la pantalla de inicio de la aplicacion.
9.3.2 Usuarios
En las figuras 9.9 y 9.10 se muestra la pantalla de inicio de la aplicacion.
124 Capıtulo 9
Figura 9.2: API - Servicio de usuarios.
9.3.3 Imagenes
En las figuras 9.11 y 9.12 se muestran los listados de imagenes y su busquedas por
texto.
9.3.4 Almacenar Imagenes del usuario
En las figuras 9.14, 9.13, 9.15 y 9.16 se muestran los listados de imagenes de un
usuario, el formulario de creacion y las edicion y eliminacion.
9.3.5 Metadatos
En las figuras 9.17 y 9.18 se muestran los listados de metadatos.
9.3.6 Busquedas por imagenes
En las siguientes capturas se muestran los resultados de busqueda por imagenes.
En la figura 9.20 se realiza la busqueda de contenido de un chip. En los resultados
mostrados en la figura 9.21 se obtiene el primer caso de exito donde la imagen similar
es un circuito integrado. En los demas resultados, al no haber mas fotografıas de
circuitos integrados, son imagenes coincidentes pero que no se relacionan con la
busqueda.
Solucion desarrollada 125
Figura 9.3: API - Recurso de imagenes.
En el ejemplo de la busqueda de la golet 9.22 se obtienen resultados positivos. Las
primeras imagenes encontradas 9.23, la primera es la misma que la buscada (pun-
tuacion cero) y los demas resultados, imagenes de la misma embarcacion en otras
perspectivas. Los siguientes resultados, figuras 9.24 y 9.25 muestran mas imagenes
con temas de paisajes marıtimos.
126 Capıtulo 9
Figura 9.4: API - Recurso de imagenes de usuarios.
Solucion desarrollada 127
Figura 9.5: API - Recurso de Metadatos.
128 Capıtulo 9
Figura 9.6: API - Recurso para las busquedas por contenido de imagen.
Solucion desarrollada 129
Figura 9.7: API - Recurso de resultados de las busquedas.
130 Capıtulo 9
Figura 9.8: Pagina de inicio de la aplicacion.
Figura 9.9: Interfaz - Login de usario.
Solucion desarrollada 131
Figura 9.10: Interfaz - Registro de usuario.
Figura 9.11: Interfaz - Listado de imagenes.
132 Capıtulo 9
Figura 9.12: Interfaz - Busqueda de imagenes por texto.
Figura 9.13: Interfaz - Subir imagen.
Solucion desarrollada 133
Figura 9.14: Interfaz - Listado de imagenes de usuario.
Figura 9.15: Interfaz - Editar imagenes de usuario.
134 Capıtulo 9
Figura 9.16: Interfaz - Eliminar imagen de usuario.
Figura 9.17: Interfaz - Listado de metadatos.
Solucion desarrollada 135
Figura 9.18: Interfaz - Busqueda de metadatos.
Figura 9.19: Busqueda por contenido de imagenes.
136 Capıtulo 9
Figura 9.20: Busqueda de imagen de chip.
Solucion desarrollada 137
Figura 9.21: Resultados de busqueda de chip.
Figura 9.22: Busqueda de goleta.
138 Capıtulo 9
Figura 9.23: Resultados de busqueda de goleta.
Solucion desarrollada 139
Figura 9.24: Resultados de busqueda de goleta.
140 Capıtulo 9
Figura 9.25: Resultados de busqueda de goleta.
10CONCLUSIONES Y TRABAJO FUTURO
10.1 CONCLUSIONES
En el trabajo del fin de master se ha alcanzado el objetivo principal, desarrollar una
plataforma eficiente y escalable para el almacenamiento y recuperacion de grandes
colecciones de imagenes. Este objetivo principal se desglosa en los siguientes objeti-
vos particulares:
• La creacion de una plataforma facil de escalar y tolerante a fallos.
• Recuperacion de imagenes basada en contenido semantico.
• Estudio de varios descriptores para su extraccion e indexacion.
• Creacion de un proyecto web completo con metodologıas agiles y nuevas tec-
nologıas para Java 8.
141
142 Capıtulo 10
• Creacion de servicio web back-end con una API con arquitectura REST com-
pleta.
• Creacion de un front-end con tecnologıas de JavaScript.
Con respecto a las decisiones de diseno durante el trabajo se pueden extraer las
siguientes conclusiones. Gracias al uso de paquetes de soporte como DataStax, con
su amplia documentacion y recursos en lınea, se ha podido crear de forma sencilla
un sistema escalable con Cassandra. JHipster ha facilitado en gran medida la crea-
cion de un proyecto inicial, con los Java Beans de Spring listos y una interfaz de
usuario basica sobre la que empezar a desarrollar. Con la librerıa MetadataExtractor
[Noakes, 2015] se han podido extraer los metadatos de imagenes. Por ultimo, Lire
[Lux, Mathias and Chatzichristofis, Savvas A., 2015a] facilita una serie de descrip-
tores, incluido algunos estandares MPEG-7 [Sikora, 2001], que permiten realizar la
extraccion de caracterısticas semanticas de una imagen e indexar sus vectores como
documentos en Lucene para posteriormente realizar busquedas por a traves de in-
vertidos basados en espacios metricos. Los resultados de las busquedas no siempre
seran satisfactorias desde el punto de vista del usuario (es decir, ante una consul-
ta, se pueden mostrar resultados que no estan relacionados), aunque esta situacion
se debe a las caracterısticas del propio modelo de extraccion de caracterısticas y
busqueda. Con respecto a esto, se podrıan tener en cuanta los siguientes aspectos:
• El exito de los resultados no reside en la cantidad de imagenes, sino en la
variedad. Cuanto mayor sea la variedad de los distintos tipos de imagenes, los
resultados seran mejores.
• Se podrıa mejorar la efectividad de la extraccion con descriptores robustos
como SIFT (Scale Invariant Feature Transform), que no se ve afectado al
reescalado o giros de las imagenes.
• Categorizar las imagenes mediante el uso de analisis de datos con aprendizaje
automatico mejorarıan notablemente los resultados de busquedas.
Conclusiones y trabajo futuro 143
10.2 TRABAJO FUTURO
La aplicacion aun tiene mejoras que pueden ser incorporadas, ası como nuevas fun-
cionalidades y control de nuevas versiones. Las mejoras que no han sido incluidas
son en el desarrollo del trabajo son:
• Recuperacion de imagenes basadas en contenido a traves de Solr: los vectores
de los descriptores de las imagenes estan almacenados en el sistema de ficheros
con Lucene a traves de la librerıa de Lire. Dado que los datos de los vectores
tambien se almacenan en Cassandra, serıa posibles realizar las busquedas en
Solr, adaptando el esquema de los ındices de Solr y el manejador utilizado por
defecto. Existe un proyecto de creacion de manejadores de Lire en Solr llamado
LireSolr [Lux, Mathias and Chatzichristofis, Savvas A., 2015b] que podrıa ser
integrando en el sistema.
• Clasificacion de imagenes con aprendizaje automatico: DataStax Enterprise
Edition permite desplegar los nodos de Cassandra en un sistema de ficheros
HDFS de Hadoop por lo que serıa posible realizar analisis de los vectores de
los descriptores de las imagenes en nodos de Cassandra cluster de Hadoop o
Apache Spark. Ademas la opcion DSE Analytics incluye librerıas como Apache
Mahout de Hadoop o MLlib de Spark [DataStax, 2015d].
• Creacion de una API HATEOAS y HAL-JSON: HATEOAS es la abreviacion
de Hypermedia as the Engine of Application State (hipermedia como motor
del estado de la aplicacion). Es una restriccion de la arquitectura de la aplica-
cion REST que lo distingue de la mayorıa de otras arquitecturas. Un cliente
interactua con una aplicacion de red a traves del hipermedia proporcionados
dinamicamente por el servidor.
El formato HAL-JSON (Hypermedia Application Language JSON) es una
especificacion abierta que describe una estructura generica para los recursos
REST. Hace la API sea autodescriptiva y su documentacion facilmente visible
desde la propia API. En resumen, hace mas facil de entender la API tanto
para desarrollares y usuarios.
144 Capıtulo 10
• Mejoras de autenticacion e integridad: con OAuth2 y JWT (JSON Web To-
ken). El sistema de autenticacion de acceso a la API es mediante un token
de sesion, para ello se crean tablas adicionales en Cassandra y se asocian al
usuario. Sin embargo, existen protocolos como OAuth21 que se centran en la
simplicidad del cliente mientras que proporciona una autorizacion especıfica
para usar en aplicaciones web, de escritorio, telefonos moviles y otros disposi-
tivos.
JWT2 es un metodo abierto, definido como estandar RFC 7519, para la repre-
sentacion de las peticiones de forma segura entre dos partes (cliente-servidor).
Un servidor con JWT decodificar, verificar y generar tokens JWT para las
peticiones, anadiendo una capa extra de seguridad y optimizando los recursos
(sustituye cadenas de texto JSON por firmas codificadas).
1http://oauth.net/2/2http://jwt.io/
BIBLIOGRAFIA
[git, ] Web oficial github. Website. https://www.github.com/.
[Albela Perez, 2005] Albela Perez, D. (2005). Spring boot data rest sample. Web-
site. https://github.com/dalbelap/spring-boot-datarest-images-sample.
[Alman, Ben and Development Team, 2015] Alman, Ben and Development Team
(2015). Web oficial grunt. Website. http://gruntjs.com/.
[Amato, Giuseppe and Savino, Pasquale, 2008] Amato, Giuseppe and Savino, Pas-
quale (2008). Approximate similarity search in metric spaces using inverted files.
In Proceedings of the 3rd International Conference on Scalable Information Sys-
tems, InfoScale ’08, pages 28:1–28:10, ICST, Brussels, Belgium, Belgium. ICST
(Institute for Computer Sciences, Social-Informatics and Telecommunications En-
gineering).
[Apache, 2015a] Apache (2015a). Hive. Website. https://pig.apache.org/.
[Apache, 2015b] Apache (2015b). Hive. Website. https://hive.apache.org/.
[Apache, 2015c] Apache (2015c). Lucene. Website. http://lucene.apache.org/.
[Apache, 2015d] Apache (2015d). Maven. Website. http://maven.apache.org.
[Bai, Yang et al., 2009] Bai, Yang, Guo, Lihua, Jin, Lianwen, and Huang, Qinghua
(2009). A novel feature extraction method using pyramid histogram of orientation
gradients for smile recognition. In Proceedings of the 16th IEEE International
Conference on Image Processing, ICIP’09, pages 3269–3272, Piscataway, NJ, USA.
IEEE Press.
[Boner, Jonas et al., 2013] Boner, Jonas, Farley, Dave, Kuhn, Roland, and Thom-
pson, Martin (2013). The reactive manifesto.
145
146 BIBLIOGRAFIA
[Brains, 2015] Brains, J. (2015). Intellij idea. Website. https://www.jetbrains.
com/idea/.
[CGlib, 2005] CGlib (2005). Cglib - byte code generation library. Website. https:
//github.com/cglib/cglib.
[Chatzichristofis, Savvas A. and Boutalis, Yiannis S., 2008] Chatzichristofis, Sav-
vas A. and Boutalis, Yiannis S. (2008). Cedd: Color and edge directivity
descriptor: A compact descriptor for image indexing and retrieval. In Proceedings
of the 6th International Conference on Computer Vision Systems, ICVS’08,
pages 312–322, Berlin, Heidelberg. Springer-Verlag.
[Cutting, 2015a] Cutting, D. (2015a). Apache. Website. http://lucene.apache.
org/solr/.
[Cutting, 2015b] Cutting, D. (2015b). Hadoop. Website. https://hadoop.apache.
org/.
[DataStax, 2005] DataStax (2005). Datastax community edition — apache cassan-
dra. Website. http://www.planetcassandra.org/cassandra/.
[DataStax, 2015a] DataStax (2015a). Datastax. Website. http://www.datastax.
com.
[DataStax, 2015b] DataStax (2015b). Datastax community 2.1. Web-
site. http://docs.datastax.com/en/cassandra/2.1/cassandra/
gettingStartedCassandraIntro.html.
[DataStax, 2015c] DataStax (2015c). Datastax enterprise. Website. http://www.
datastax.com/products/datastax-enterprise.
[DataStax, 2015d] DataStax (2015d). Datastax enterprise analytics. Web-
site. http://docs.datastax.com/en/datastax_enterprise/4.7/datastax_
enterprise/anaHome/anaHomeTOC.html.
[Dubois and Mirc, 2015] Dubois, J. and Mirc, J. (2015). Jhipster. Website. http:
//jhipster.github.io/.
Conclusiones y trabajo futuro 147
[Ebay, 2012] Ebay (2012). Cassandra data modeling best practices.
[Foundation, 2015] Foundation, N. (2015). Web oficial nodejs. Website. https:
//nodejs.org/en/.
[Fowler, 2010] Fowler, M. (2010). Richardson maturity model: Steps toward the
glory of rest.
[Google, 2005] Google (2005). Google image search. Website. https://www.
google.es/imghp.
[Google, 2015a] Google (2015a). Angularjs. Website. https://angularjs.org/.
[Google, 2015b] Google (2015b). Angularjs-resource. Website. https://docs.
angularjs.org/api/ngResource/service/$resource.
[Hornton, Jacobt and MacCaw, Alex, 2015] Hornton, Jacobt and MacCaw, Alex
(2015). Web oficial bower. Website. http://bower.io/.
[ImageRaider, 2005] ImageRaider (2005). Imageraider. Website. https://www.
imageraider.com/.
[JGraph, 2005] JGraph (2005). Draw.io. Website. https://www.draw.io/.
[Jing Huang et al., 1997] Jing Huang, S. Ravi Kumar, Mandar Mitra, Wei-Jing Zhu,
and Ramin Zabih (1997). Image indexing using color correlograms. 2014 IEEE
Conference on Computer Vision and Pattern Recognition, 0:762.
[Lakshman, Avinash and Malik, Prashant, 2010] Lakshman, Avinash and Malik,
Prashant (2010). Cassandra: A decentralized structured storage system. SIGOPS
Oper. Syst. Rev., 44(2):35–40.
[Luciani, 2014] Luciani, J. (2014). Improved cassandra 2.1 stress tool: Benchmark
any schema.
[Lux, 2011] Lux, M. (2011). Content based image retrieval with lire. In Proceedings
of the 19th ACM International Conference on Multimedia, MM ’11, pages 735–
738, New York, NY, USA. ACM.
148 BIBLIOGRAFIA
[Lux, Mathias and Chatzichristofis, Savvas A., 2008] Lux, Mathias and Chatzi-
christofis, Savvas A. (2008). Lire: Lucene image retrieval: An extensible java cbir
library. In Proceedings of the 16th ACM International Conference on Multimedia,
MM ’08, pages 1085–1088, New York, NY, USA. ACM.
[Lux, Mathias and Chatzichristofis, Savvas A., 2015a] Lux, Mathias and Chatzi-
christofis, Savvas A. (2015a). Lire: Lucene image retrieval. Website. http:
//www.lire-project.net/.
[Lux, Mathias and Chatzichristofis, Savvas A., 2015b] Lux, Mathias and Chatzi-
christofis, Savvas A. (2015b). Lire solr plugin. Website. https://bitbucket.
org/dermotte/liresolr.
[Morling, 2005] Morling, G. (2005). Mapstruct. Website. http://mapstruct.org/.
[Noakes, 2015] Noakes, D. (2015). Metadata extractor. Website. https://
drewnoakes.com/code/exif/.
[Osmani et al., 2015] Osmani, A. et al. (2015). Web oficial yeoman. Website. http:
//yeoman.io/.
[Otto, 2015] Otto, M. (2015). Web oficial bootstrap. Website. http://
getbootstrap.com/.
[Park, Dong Kwon et al., 2000] Park, Dong Kwon, Jeon, Yoon Seok, andWon, Chee
Sun (2000). Efficient use of local edge histogram descriptor. In Proceedings of
the 2000 ACM Workshops on Multimedia, MULTIMEDIA ’00, pages 51–54, New
York, NY, USA. ACM.
[PlanetCassandra, 2015] PlanetCassandra (2015). Apache cassandra nosql
performance benchmarks. Website. http://www.planetcassandra.org/
nosql-performance-benchmarks/.
[Qi, Xiaojun and Allan, Steve, 2007] Qi, Xiaojun and Allan, Steve (2007).
Semantic-based cbir (content-based image retrieval).
[Richardson, 2015] Richardson, L. (2015). Richardson maturity model restful. Web-
site. http://restcookbook.com/Miscellaneous/richardsonmaturitymodel/.
Conclusiones y trabajo futuro 149
[Richardson, Leonard and Ruby, Sam, 2007] Richardson, Leonard and Ruby, Sam
(2007). Restful Web Services. O’Reilly, first edition.
[Shvachko, Konstantin et al., 2010] Shvachko, Konstantin, Kuang, Hairong and Ra-
dia, and Sanjay and Chansler, Robert (2010). The hadoop distributed file system.
In Proceedings of the 2010 IEEE 26th Symposium on Mass Storage Systems and
Technologies (MSST), MSST ’10, pages 1–10, Washington, DC, USA. IEEE Com-
puter Society.
[Sikora, 2001] Sikora, T. (2001). The mpeg-7 visual standard for content description,
an overview.
[Spring, 2015a] Spring (2015a). Spring. Website. https://www.spring.io.
[Spring, 2015b] Spring (2015b). Spring boot. Website. http://projects.spring.
io/spring-boot/.
[Spring, 2015c] Spring (2015c). Spring mvc. Website. http://docs.spring.io/
spring-framework/docs/current/spring-framework-reference/html/mvc.
html.
[TinEye, 2005] TinEye (2005). Tineye reverse image search. Website. https://
www.tineye.com/.
[TypeSafe, 2005] TypeSafe (2005). Scala play framework. Website. https://www.
playframework.com/.
150 BIBLIOGRAFIA
MANUAL DE INSTALACION
A continuacion se describe la instalacion del sistema de DataStax Enterprise en
GNU/Linux Debian Jessie. En primer lugar se recomienda actualizar los paquetes
de Debian. Para ello realizar los siguientes pasos.
.1 INSTALACION PAQUETES BASICOS
1. Instalacion del JDK de Java 8 y Apache Maven 3.
2. Instalacion del paquete de DataStax Enterprise
Para la instalacion de Cassandra con DataStax se utiliza el manual de insta-
lacion para los paquetes de Debian3 de la pagina oficial.
Para poder descargar los paquetes de instalacion se debe ir al sitio web de
DataStax, pulsar en descargar producto y registrarse4. Los datos del registro
seran necesarios para descargar los paquetes de la instalacion en Debian.
# Repositorio de DataStax , acceso con usuario y
password del registro en la web
$ echo "deb http :// username:password@debian.datastax.
com/enterprise stable main" | sudo tee -a /etc/apt
/sources.list.d/datastax.sources.list
# public key
$ curl -L https :// debian.datastax.com/debian/repo_key
| sudo apt -key add -
$ sudo apt -get update
$ sudo apt -get install dse -full (Use for all product
levels .)
3Using Datastax: http://docs.datastax.com/en/cassandra/2.1/cassandra/install/installDeb t.html4https://academy.datastax.com/downloads?destination=downloads&dxt=DX
151
152 .1. INSTALACION PAQUETES BASICOS
$ sudo apt -get install dse -full opscenter (Also
installs OpsCenter .)
3. Configuracion de Cassandra
$ sudo service cassandra stop
$ sudo rm -rf /var/lib/cassandra/data/system /*
# Editar Cassandra DSE en Modo Solr
$ sudo vim /etc/default/dse
SOLR_ENABLED =1
# Iniciar servicios
$ sudo service dse start
$ sudo service datastax -agent start
$ sudo nodetool status
4. Creacion del KeySpace y tablas
Desde la ruta del proyecto, se debe crear el “keyspace” y las tablas de Cassan-
dra.
$ cqlsh -f src/main/resources/config/cql/create -
keyspace -prod.cql
$ cqlsh -k flipper -f src/main/resources/config/cql/
create -tables.cql
$ cqlsh -k flipper -f src/main/resources/config/cql/
Picture.cql
$ cqlsh -k flipper -f src/main/resources/config/cql/
Metadata.cql
$ cqlsh -k flipper -f src/main/resources/config/cql/
PictureSearch.cql
$ cqlsh -k flipper -f src/main/resources/config/cql/
PictureFound.cql
5. Indexacion de las tablas en Solr con dsetools Las tablas que sean utilizadas
con Solr deberan ser configuradas en Solr. Con DataStax se puede realizar
de forma automatica con la herramienta dsetool. En la ruta sold config del
proyecto se encuentran los ficheros de configuracion para la indexacion de las
tablas.
$ cd solr_config
$ for i in ‘ls config *.yaml ‘; do echo "## $i"; cat $i
; done
Manual de instalacion 153
## config -metadata.yaml
default_query_field: description
auto_soft_commit_max_time: 1000
generate_docvalues_for_fields: ’*’
enable_string_copy_fields: false
## config -pictureFound.yaml
default_query_field: title
auto_soft_commit_max_time: 1000
generate_docvalues_for_fields: ’*’
enable_string_copy_fields: false
## config -pictureSearch.yaml
default_query_field: created
auto_soft_commit_max_time: 1000
generate_docvalues_for_fields: ’*’
enable_string_copy_fields: false
## config -picture.yaml
default_query_field: title
#directory_factory_class: Similarity
auto_soft_commit_max_time: 1000
generate_docvalues_for_fields: ’*’
enable_string_copy_fields: false
$ sudo dsetool create_core flipper.picture
generateResources=true reindex=true coreOptions=
config -picture.yaml
$ sudo dsetool create_core flipper.metadata
generateResources=true reindex=true coreOptions=
config -metadata.yaml
$ sudo dsetool create_core flipper.picturesearch
generateResources=true reindex=true coreOptions=
config -pictureSearch.yaml
$ sudo dsetool create_core flipper.picturefound
generateResources=true reindex=true coreOptions=
config -pictureFound.yaml
6. Ejecucion del proyecto en modo produccion Desde la ruta del proyecto ejecutar
$ mvn compile
$ mvn -Pprod spring -boot:run
7. Ejecucion de Grunt Desde la ruta del proyecto ejecutar
$ sudo apt -get install nodejs
$ npm -g bower grunt -cli
$ grunt serve
154 .1. INSTALACION PAQUETES BASICOS
Al arrancar la aplicacion, se abre automaticamente una ventana del navegador
en la direccion http://localhost:3000.