Existen diferentes anotaciones que podemos utilizar en nuestras entidades para dar formato a los valores de sus propiedades. En esta ocasión, hablaremos de las anotaciones @Type, @JsonFormat y @Temporal.



  • @Type: con esta anotación podemos cambiar el tipo de dato que viene de base de datos y convertirlo en otro tipo de dato. Como ejemplo, hemos añadido la propiedad ebook anotada en nuestra entidad Libro, con el fin de conocer si es un libro electrónico o no. En base de datos hemos utilizado una columna de tipo SMALLINT para representar valores boolean (0 y 1). Sin embargo, la propiedad en la entidad es de tipo boolean. Para realizar esta conversión de número a boolean tenemos que utilizar la anotación @Type especificando el tipo de conversión, que en este caso es org.hibernate.type.NumericBooleanType, tal y como se muestra en la línea 34. 
  • @JsonFormat: esta anotación permite formatear el valor de una propiedad en el momento de serialización de los datos. Hemos utilizado esta anotación para dar formato a la propiedad timestampInsercion, que indica la fecha de inserción de un libro en base de datos. Así, cuando obtenemos el JSON de un libro, la propiedad timestampInsercion se formateará siguiendo el patrón y el horario GMT especificado, tal y como se puede apreciar en la línea 38. 
  • @Temporal: sirve para especificar la precisión temporal de una propiedad de tipo Date. De esta forma, puede ser de tipo TIMESTAMP, TIME o DATE. En el ejemplo de la línea 39, hemos especificado una precisión de tipo TIMESTAMP, ya que es lo que almacenaremos en base de datos.


Pulsa aquí para acceder al código de esta entrada en mi repositorio Github
Spark dispone de un módulo para el procesamiento de consultas SQL. Estas consultas pueden utilizarse para manipular datos estructurados de lo más variado (JSON, CSV, Parquet, etc.) y sobre multitud de repositorios (bases de datos relacionales y no relacionales, Amazon S3, Hadoop, etc.). Además, es compatible con HiveQL, un lenguaje similar al conocido SQL que se ha convertido en indispensable a la hora de realizar operaciones sobre grandes cantidades de datos en plataformas Hadoop.



En este ejemplo, veremos cómo realizar consultas SQL sobre documentos en formato JSON. Para ello puedes descargar el fichero personas.json, donde en cada línea encontrarás la representación de una entidad Persona con los atributos nombre, edad y género. Es importante recalcar que en cada línea se encuentra una única representación de la entidad Persona en JSON, ya que de lo contrario el fichero no podría ser procesado por Spark. Debes copiar este fichero en "C:", para poder hacer referencia al mismo en el código del programa (línea 19) que leerá las personas contenidas en dicho fichero.

Para tener acceso al fichero y poder realizar las consultas, se crea un HiveContext. Esta clase dispone de métodos para leer archivos de diferentes tipos, en este caso JSON. Se puede especificar un fichero o un directorio que contiene ficheros JSON. En  nuestro caso será un único fichero. La lectura de este fichero devuelve una estructura de tipo DataFrame. Este tipo de dato es similar al utilizado en R y viene a representar los datos como si de una tabla de base de datos se tratara. El esquema de los datos y su tipología se infieren automáticamente por Spark, aunque para ganar en eficiencia pueden ser especificados de forma manual.

Una vez que tenemos el DataFrame, se creará una tabla temporal cacheada (líneas 22 y 23) sobre la que poder lanzar las consultas SQL. Una primera consulta obtiene todas las personas del fichero, y una segunda obtiene el número de personas del fichero. Los datos se devuelven en una estructura de tipo DataFrame.

A continuación hemos utilizado un método de agregación proporcionado por el DataFrame. Este método devuelve el máximo, mínimo y número de personas en un DataFrame.



Podemos visualizar los resultados con la función show() que dibujará por consola los DataFrame.



Pulsa aquí para acceder al código de esta entrada en mi repositorio GitHub
Hasta ahora, el método con el que obteníamos los libros existentes en nuestro repositorio nos devolvía un JSON con todos los libros almacenados. Esto puede ser un problema si nuestro repositorio contiene demasiados libros, por lo que sería interesante poder realizar una paginación y ordenación de resultados.

Afortunadamente, Spring Framework cuenta con los mecanismos necesarios, no sólo para obtener resultados paginados, sino también para obtener dichos resultados ordenados en base a unos campos determinados.



Para ello, lo primero que tenemos que hacer es cambiar la clase de la que extiende nuestro repositorio. Ahora deberá extender de la clase PagingAndSortingRepository, que ofrece además de los métodos ya comentados, la posibilidad de realizar una paginación y ordenación de resultados.




Además, el fichero dispatcher-servlet.xml debe ser modificado para que el sistema MVC de Spring Framework sea capaz de interpretar las peticiones de paginación y ordenación de forma correcta. Para ello se añadirá a la configuración el bean PageableHandlerMethodArgumentResolver.



También cambiará la llamada de la clase LibroFacade. Ahora, el parámetro de entrada será un objeto de tipo Pageable y devolverá una página de libros Page<Libro>



Este cambio de parámetros debe también ser reflejado en el controlador LibroController que inicia el procesamiento de la llamada a nuestra API RESTful.



Una vez que hayamos configurado nuestra API RESTful, podremos realizar llamadas que requieran de paginación de la siguiente forma
GET /libros?page=0&size=1

Para realizar llamadas con ordenación serán de la siguiente forma
GET /libros?sort=titulo,asc

Estos parámetros de paginación y ordenación, pueden combinarse también. A continuación se muestra un ejemplo del resultado obtenido con una llamada de paginación y ordenación como la que sigue.

http://localhost:8080/spring.rest.jpa.ii/v1/libros?page=0&size=2&sort=titulo,asc

Este resultado contiene ciertos parámetros adicionales que informan del total de páginas de las que se dispone (totalPages), el total de elementos (totalElements) o si es la última página (last), además de devolver los libros que pertenecen a la página de resultados solicitada.


En esta ocasión hablaré del tratamiento de excepciones y de los códigos de respuesta de nuestra API RESTful. Si recordamos la primera versión de la API, no teníamos ningún mecanismo que informara al cliente de que un determinado recurso no existía. Si por ejemplo realizábamos la petición GET /libro/87, el servidor no devolvería ningún resultado, pero el código de respuesta sería 200 (OK).

Para este caso concreto, lo correcto sería devolver un código de respuesta 404 (Not found). Puedes encontrar más información sobre los diferentes tipos de códigos de respuesta aquí



Para proveer a nuestros servicios de los mecanismos de tratamiento de excepciones y generación de códigos de respuesta necesarios, hemos creado un nuevo paquete de nombre spring.rest.jpa.ii.exception en el que se han implementado dos clases.
  • ResourceNotFoundException: es la excepción que extiende de RuntimeException y que se lanzará cada vez que se pida un recurso inexistente. En el constructor, podemos pasar el identificador del recurso que se ha solicitado pero que no existe en base de datos.


  • RestExceptionController: es el controlador que gestionará las excepciones lanzadas en los controladores de nuestra API. Es por eso que se ha utilizado la anotación @ControllerAdvice en la línea 21. La clase dispone de un método llamado resourceNotFound que se invocará para gestionar la excepción lanzada. El método utiliza las siguientes anotaciones.
    • @ExceptionHandler: con esta anotación informamos del tipo de excepción que gestionará el método, en este caso ResourceNotFoundException
    • @ResponseStatus: indicamos el código de respuesta a devolver, en este caso un 404.
    • @ResponseBody: para incluir un mensaje informativo en referencia al recurso inexistente.


De esta forma, si realizamos la petición GET /libro/87, se devolverá un código de respuesta 404 indicando que el recurso no existe, además de un mensaje del tipo "El recurso [87] no ha sido encontrado".


Pulsa aquí para acceder al código de esta entrada en mi repositorio Github
En esta segunda entrada sobre Apache Spark veremos como ejemplo el "Hello world" del mundo Big Data: contar las palabras de un texto y ver qué palabras son las más utilizadas en el mismo. Para ello, utilizaremos un archivo de texto que contiene todas las obras de William Shakespeare descargable desde aquí (aunque ya lo incluyo en el proyecto que puedes clonar desde mi repositorio GitHub).



Es importante señalar que para poder probar este ejemplo, necesitarás descargar un archivo llamado winutils.exe en este enlace, y copiarlo en la ruta "C:\hadoop\bin", de lo contrario la ejecución del código dará el mensaje de error Failed to locate the winutils binary in the hadoop binary path. Además, debes copiar el archivo shakespeare.txt en "C:".

En el código, hemos definido en la línea 20 una función para eliminar espacios en blanco adicionales, signos de exclamación, interrogación, puntuación, etc. con el fin de limpiar el fichero de texto. Esta función la hemos aplicado sobre el texto en la línea 30 utilizando la función map

Posteriormente obtenemos por cada línea las palabras y eliminamos aquellas que estén en blanco (línea 36) utilizando las funciones flatMap y filter. En la línea 38 obtenemos el número de palabras totales.

Ahora vamos a contar las apariciones de cada palabra en el texto, para lo cual utilizaremos el paradigma mapreduce mediante las funciones que nos ofrece Apache Spark. Para ello, convertiremos cada palabra en el par (palabra, 1) mediante la función map (línea 41). Posteriormente, utilizaremos la función reduceByKey para agrupar estos pares en base a su clave, que es la palabra, y contabilizar el número de apariciones de cada una sumando el valor de cada par (línea 42).

Para finalizar, imprimiremos por pantalla las 50 palabras que más se repiten en el texto. Para ordenar las palabras de mayor a menor número de apariciones, hemos utilizado en la línea 50 la función shortWith, que ordena en base al criterio establecido los pares (palabra, num. apariciones).


Pulsa aquí para acceder al código de esta entrada en mi repositorio GitHub
Seguimos con el tutorial para el desarrollo de una API RESTful con Spring Framework y JPA. Después de haber abordado aspectos básicos de la implementación de la API, en esta y siguientes entradas trataré aspectos algo más avanzados.

Para ello, he creado un nuevo proyecto con algunas modificaciones y mejoras sobre el anterior, con el fin de que sirva como ejemplo de todo lo que comentaré en siguientes entradas. En esta entrada, empezaré tratando algo fundamental en cualquier desarrollo de APIs RESTful, como es el versionado de los servicios de la API. 



Existen diferentes alternativas para implementar un mecanismo de versiones de nuestra API. Desde versionado por parámetro en la propia URL, petición de una versión específica de un recurso en el Header de la petición, etc. 

La estrategia que creo que es más sencilla de implementar y la que podemos ver en muchas APIs como Twitter y Facebook, es la basada en el versionado mediante la propia URL. En base a esta estrategia de versionado, las peticiones a nuestra API quedarán de la forma v1/libros, variando en función de la versión del servicio el path de la petición (v1, v2, etc.).

Para implementar el versionado, debemos modificar el controlador, especificando la versión en la raíz del path (línea 27). Además, nombraremos el controlador con un nombre específico para esa versión, en este caso libroController.v1 (línea 26). De esta forma, podremos disponer de diferentes versiones del controlador distribuidas en paquetes distintos de nuestro proyecto sin que entren en conflicto.



Pulsa aquí para acceder al código de esta entrada en mi repositorio GitHub
Es el turno de visualizar datos gracias a Kibana. Este entorno de visualización web es la tercera pata del stack ELK. Gracias a Kibana, podremos realizar diferentes visualizaciones en base a los datos indexados en Elasticsearch. Además, podremos configurar paneles de control o dashboards creados a partir de las visualizaciones creadas.

Para esta entrada, necesitaremos descargar y descomprimir Kibana.


Puesta en marcha de Kibana

Kibana se conectará a la instancia de Elasticsearch que ya hemos configurado en entradas anteriores, mediante el host y puerto configurados por defecto, por lo que tendremos que tener activa dicha instancia de Elasticsearch.

Para iniciar Kibana, accederemos a la carpeta bin dentro del directorio raíz de Kibana, y ejecutaremos el archivo kibana.bat. Accederemos a la consola de gestión en la URL http://localhost:5601/

Configuración de índices

El primer paso para empezar a trabajar con Kibana es configurar los índices de Elasticsearch desde donde se obtendrán los datos para ser visualizados. Esta configuración se realiza en la pestaña Settings. En nuestro ejemplo, los datos de cotización en bolsa los hemos almacenado en el índice de nombre logstash-bolsa-2015.

En la casilla para la especificación del índice, pondremos "logstash-bolsa-*". En vez de utilizar el nombre exacto del índice, hemos utilizado el asterisco como patrón, para que nos sirva para futuros datos referentes a otros años (p. ej. logstash-bolsa-2016).

Indicaremos también que nuestro índice dispone de una propiedad que almacena un dato de tipo timestamp, que en este caso corresponde a la fecha de cotización en bolsa. Indicaremos también que la propiedad que almacena dicho dato es @timestamp.

Crearemos el índice e inmediatamente veremos un listado con las propiedades que Kibana ha encontrado en los documentos almacenados en dicho índice. Podremos ver algunas propiedades adicionales de Elasticsearch junto con las propiedades de los valores de cotización en bolsa.



 

Exploración de datos

Para echar un vistazo rápido a los datos, podremos ir a la sección Discover para realizar algunas consultas. Disponemos de una caja de texto con un asterisco, que indica que queremos obtener todos los datos indexados.

Puesto que los valores de cotización en bolsa corresponden al año 2015, tendremos que configurar el rango temporal con el control disponible arriba a la derecha. Mediante la opción Quick -> Previous year podremos ver un gráfico que refleja el histograma de los datos recogidos ese año y un listado con una muestra de los mismos.

También podemos filtrar los datos en base al valor de una propiedad. Por ejemplo, podemos especificar en el espacio de búsqueda "company: YAHOO", con lo que veremos únicamente los datos correspondientes a las acciones de Yahoo.


  

Visualización de datos

Vamos a crear ahora una visualización de datos, mediante la pestaña Visualize. En esta pestaña, veremos un listado de posibles gráficas. En este caso, vamos a crear una gráfica de tipo Line Chart, con el fin de comparar el precio de las acciones de cada compañía en el momento de apertura de mercados. Pinchando en Line Chart y seleccionando From a new search, accederemos a la configuración de la gráfica.

En el eje Y, elegiremos como métrica el máximo sobre el campo open. De esta forma, el eje Y representará los valores de las acciones en el momento de apertura de mercados. Para el eje X, crearemos lo que se denominan buckets. Los buckets son una especie de "cubos" donde se "insertarán" o agruparán los datos en base a una serie de parámetros. 

En nuestro caso, crearemos un bucket de tipo Date histogram sobre la propiedad @timestamp, para que los datos se puedan agrupar en base a la fecha en la que fueron obtenidos. Si dejáramos así la configuración y ejecutáramos la visualización (botón verde con el play) veríamos una gráfica con una sola línea, que correspondería a los valores de Google, ya que es la que dispone de los valores más altos.

Para solucionar el problema, añadiremos un sub-bucket sobre el ya configurado. Este sub-bucket será de tipo Split Lines, para diferenciar las líneas correspondientes a Google y Yahoo. Para ello, crearemos un par de filtros, que servirán para dividir la línea en dos. Los filtros pueden configurarse en base a valores de las propiedades, por lo que crearemos uno de tipo "company : YAHOO" y otro de tipo "company : GOOGLE". Ejecutando otra vez la visualización, veremos que ahora aparecen dos líneas, una para cada compañía. Podremos guardar la visualización para su posterior uso o consulta.


Creación de un panel de control o dashboard

En la pestaña Dashboard podemos crear un panel de control con las gráficas que hemos guardado anteriormente. En este caso, crearemos un panel de control que incluya la visualización creada anteriormente. Podemos situar cada visualización en el lugar que creamos pertinente y con las dimensiones que queramos, por lo que se permite una flexibilidad total a la hora de componer nuestros paneles de control. Estos dashboard podemos también guardarlos e incluso incrustarlos en cualquier web externa mediante la opción Share, la cual nos facilita un código de incrustación.


 

Conclusiones

Kibana permite realizar visualizaciones de datos de una forma sencilla y flexible. Dispone de multitud de tipos de gráficas y parámetros de configuración, que hacen que pueda adaptarse a cualquier dominio y necesidad de visualización de datos. Es importante recalcar, que estas visualizaciones se actualizan en tiempo real a medida que nuevos datos son indexados en Elasticsearch. Si los tipos de gráficos no fueran suficientes, se pueden desarrollar plugins de forma sencilla para crear los gráficos que podamos necesitar en cada caso.

En siguientes entradas crearé un ejemplo más completo con el stack ELK para la indexación y visualización de tweets de la red social Twitter, así que puedes suscribirte a mi blog para no perder detalle!
Después de presentar ElasticSearch, es el turno de Logstash, un recolector de datos distribuidos. Logstash fue diseñado para recopilar datos provenientes de ficheros de logs (p. ej. logs de servidores Apache, Tomcat, etc.) con el fin de pre-procesarlos y enviarlos a un sistema de almacenamiento, como es el caso de ElasticSearch. Sin embargo, la herramienta ha evolucionado mucho y ya existen multitud de plugins que nos permiten obtener datos desde fuentes de datos de lo más variado (p. ej. Twitter, ficheros, bases de datos, servicios,...), pre-procesar dichos datos aplicando filtros y redirigir los datos (p. ej. hacia repositorios de datos, sistemas de visualización, sistemas de colas, etc.)


En esta entrada veremos un ejemplo de ingesta de datos desde un fichero CSV hacia Elsticsearch mediante Logstash. Para ello, utilizaremos un fichero con valores de cotización en bolsa de las compañías Yahoo y Google durante el año 2015 que puedes descargar aquí. Si te apetece obtener datos de alguna otra compañía, puedes obtener un fichero CSV similar en este enlace.

Para poder seguir esta entrada, necesitarás las siguientes herramientas:

Flujo de trabajo con Logstash

El flujo de trabajo con Logstash se basa en la definición de entradas, filtros y salidas de datos. En las entradas (input), se define la fuente de datos desde donde obtener los datos. En los filtros (filter) se definen operaciones y transformaciones sobre los datos, y en las salidas (output) se define el destino de dichos datos. Este flujo de datos se especifica en un fichero de configuración que tiene el siguiente aspecto.


Ejemplo con Logstash

Para realizar el ejemplo, tendremos que habilitar la plataforma Elasticsearch. Después, tendremos que descargar y descomprimir Logstash. A continuación, copiaremos en la carpeta bin del directorio de Logstash el fichero CSV que utilizaremos como fuente de datos de entrada (input), que puedes descargar aquí.

Ahora tendremos que crear el fichero de configuración que utilizaremos para leer los valores de las acciones, pre-procesarlos y enviarlos a Elasticsearch para su indexación.

En el bloque correspondiente al input hemos especificado las siguientes propiedades:
  • file: para configurar una entrada de datos proveniente de un fichero.
    • path: ruta donde se encuentra el fichero.
    • start_position: posición del fichero desde la que Logstash empezará a parsear datos.
    • type: tipo de documento que se creará en Elasticsearch cuando se indexen los datos.
    • sincedb_path: utilizado para que Logstash no tenga en cuenta dónde se quedó el cursor al parsear el fichero. Esta configuración viene bien para realizar pruebas, ya que de esta forma siempre podremos volver a indexar los datos del fichero. 
    • mutate: utilizado para mapear tipos de datos.
En el bloque correspondiente a los filtros hemos configurado lo siguiente:
  • csv: para mapear las columnas del fichero CSV a propiedades del documento JSON con unos nombres determinados.
  • date: para especificar la fecha proveniente de los datos de cotización de las acciones. Para ello, especificamos el patrón que siguen las fechas en el fichero CSV mediante la propiedad match, especificamos el locale y finalmente especificamos con target la propiedad del documento JSON de salida a la que se mapeará la fecha. Si no se configura un filtro de tipo date, Elasticsearch indexará cada documento con el timestamp en el que se recibió dicho documento.
Finalmente especificamos el output o salida de los datos mediante las siguientes configuraciones:
  • elasticsearch: donde especificamos el host donde está Elasticsearch y el índice que creará cuando se indexen los datos. Este índice se ha creado utilizando un patrón, de manera que se crea un índice por cada año perteneciente a los datos de cotización en bolsa. De esta forma se podrán borrar los documentos asociados a cada año borrando para ello su índice correspondiente.
  • stdout: mostrará en consola de comandos los datos parseados y enviados a Elasticsearch con el fin de comprobar que el proceso ha ido bien.


Una vez creado el fichero, deberemos situarlo en la carpeta bin de nuestro directorio de Logstash. Para empezar el proceso, tendremos que ejecutar Logstash, abriendo una consola de comandos en el directorio bin y escribiendo el siguiente comando, donde especificamos qué fichero de configuración queremos que Logstash utilice.

Cuando termine el proceso, podremos acceder al manager de Elasticsearch (http://localhost:9200/_plugin/kopf) para comprobar que los índices se han creado y que existen datos indexados. Concretamente, veremos que se han indexado 511 documentos nuevos en el índice logstash-bolsa-2015.


Conclusiones

Logstash es muy sencillo de configurar y dispone de multitud de plugins para realizar procesos de gestión de datos, lo que le convierte en una herramienta muy flexible. En la siguiente entrada utilizaremos Kibana para crear visualizaciones sobre los datos que hemos indexado con Elasticsearch.

Estamos en la era de los datos o el denominado Big Data. Disponemos de tal cantidad de datos, que los tradicionales sistemas de almacenamiento, gestión y procesamiento de los mismos ya no son válidos. Es por ello que en los últimos años han aparecido multitud de herramientas y frameworks para gestionar grandes cantidades de datos de forma eficiente. Una de estas herramientas es Apache Spark.



En esta entrada hablaré de este framework de procesamiento y análisis distribuido de datos, mostrando una introducción básica al mismo. En siguientes entradas mostraré más funcionalidades con ejemplos más complejos.

Para seguir esta entrada se necesitan las siguientes herramientas.

¿Qué es Apache Spark?

Spark es un framework de procesamiento y análisis distribuido de grandes cantidades de datos en memoria. Dispone de diversos módulos que permiten aplicar operaciones sobre datos, realizar consultas, procesamiento de grafos, stream processing y aplicación de algoritmos de machine learning de una forma eficiente y escalable. Al ser un entorno de computación en memoria, Spark puede conectarse a repositorios de datos externos como HBase, Cassandra, MongoDB, etc. donde residen datos persistentes.

Spark está escrito en Scala, un lenguaje de programación funcional soportado por la máquina virtual Java (JVM). De esta forma, el código Scala se compila y se convierte en bytecode Java, por lo que un programa escrito en Java es perfectamente interoperable con otro escrito en Scala.

El framework Spark dispone de una API para poder implementar aplicaciones de procesamiento y análisis de datos. Esta API se encuentra soportada por lenguajes de programación como Java o Python, aunque lo más natural es utilizar Scala, ya que es el lenguaje de programación que sustenta Spark.

Arquitectura de Spark

Los principales componentes de Spark son los que se describen a continuación:
  • Workers: son los nodos donde se ejecutan de forma distribuida las operaciones o tareas de las aplicaciones Spark.
  • Cluster manager: gestiona los workers. Spark dispone de su propio manager, aunque también soporta los gestores Mesos y YARN.
  • Driver program: es la aplicación principal. Las diferentes tareas que se implementen en esta aplicación serán las que se distribuyan por los workers del clúster para la realización en paralelo de las diferentes operaciones requeridas.

API de Spark

Antes de entrar en materia y poner un sencillo ejemplo de procesamiento de datos con Spark, es conveniente entender algunos conceptos básicos de la API.
  • SparkContext: facilita la conexión de la aplicación con un clúster Spark.
  • Resilient Distributed Dataset (RDD): un RDD es una colección de elementos que puede particionarse para ser tratados en paralelo. La API proporciona métodos para conectarse a diferentes repositorios de datos (Hadoop, Cassandra, etc.) y crear RDDs. La creación de un RDD es una operación "lazy", es decir, que Spark no realiza ninguna acción al momento, sino que realiza una programación de dicha operación hasta que ocurra alguna acción sobre los datos del RDD. Las operaciones que pueden realizarse sobre un RDD son transformaciones y acciones.
  • Transformaciones: una operación de transformación sobre un RDD devuelve un nuevo RDD al que se le aplica cierta operación. Por ejemplo, aplicar una determinada función a los elementos del RDD (p. ej. multiplicar por 2 cada elemento), filtrar los elementos en base a una condición determinada, etc.
  • Acciones: las acciones devuelven un valor a la aplicación o Driver program. Por ejemplo, contar los elementos de un RDD, calcular el máximo, el mínimo o guardar el contenido de un RDD en un fichero son ejemplos de acciones sobre un RDD.

Ejemplo con Apache Spark

Para empezar a trabajar con Spark necesitaremos descargar y descomprimir el IDE Scala, que está basado en Eclipse. Para lanzar el entorno ejecutaremos el archivo eclipse.exe. Después, descargaremos el proyecto con el código fuente del repositorio GitHub en este enlace.

Ahora veremos un ejemplo muy sencillo en el que filtraremos una lista de números para obtener los que sean pares. En primer lugar crearemos un objeto llamado NumerosPares que extienda de App (línea 6) dentro del fichero de nombre NumerosPares.scala. Con esto, estamos definiendo una nueva aplicación de Scala, que ejecutará el código contenido en el propio objeto.

Configuraremos una conexión a un clúster Spark, en este caso local, mediante el objeto SparkConf, que después utilizaremos para instanciar un objeto de tipo SparkContext. Después crearemos un RDD partiendo de una lista de 10.000 números. Sobre este RDD de nombre listaNumerosRdd, realizaremos una transformación mediante un filtro. El filtro aplicará una función para obtener un nuevo RDD que solamente contenga los números pares. La notación utilizada en la función mediante la barra baja "_" es equivalente a escribir { x => x % 2 == 0} (línea 15).

Finalmente aplicaremos una acción sobre el RDD, para contar el número de elementos pares que después visualizaremos por consola.


Para ejecutar la aplicación, nos posicionaremos con el ratón en el fichero NumerosPares.scala, pulsando con el botón derecho del ratón y seleccionando Run As -> Scala Application.

Conclusiones

Spark es un entorno con múltiples funcionalidades, que simplifica enormemente la gestión de grandes cantidades de datos de forma transparente. La magia de Spark reside en que el programa escrito en el ejemplo, puede ejecutarse tanto en un clúster local, como en un clúster distribuido compuesto por multitud de nodos sin cambiar ni una sola línea de código.

En siguientes entradas veremos ejemplos más completos de la API Spark para la gestión de grandes cantidades de datos, así como otros módulos ofrecidos por el framework como los de consulta de datos o el de aplicación de algoritmos de machine learning.

Pulsa aquí para acceder al código de esta entrada en mi repositorio GitHub
Hoy hablaré de Elasticsearch, un indexador de documentos que se está haciendo muy popular gracias al stack ELK (Elasticsearch, Logstash y Kibana). Estos tres componentes son bastante comunes en arquitecturas Big Data. Logstash facilita la ingesta de datos provenientes de fuentes distribuidas, Elasticsearch indexa los datos y Kibana permite su visualización. En esta entrada mostraré las funcionalidades básicas de Elasticsearch como motor de indexación.



Las herramientas necesarias para seguir este tutorial son:

¿Qué es Elasticsearch?

Elasticsearch es un motor de búsqueda basado en Apache Lucene que permite indexar grandes cantidades de datos para su posterior consulta de forma eficiente. Los datos o documentos que se indexan no necesitan tener una estructura determinada, aunque para un mejor funcionamiento y explotación de los mismos es recomendable su definición.

Como todo sistema orientado a Big Data, es fácilmente escalable y se puede configurar de forma distribuida. Dispone de una API RESTful para interactuar con la plataforma y los datos, un lenguaje de consulta llamado Query DSL y multitud de librerías en los principales lenguajes de programación (Java, PHP, etc.).


Wikipedia, Soundcloud o GitHub utilizan Elasticsearch para proporcionar búsquedas de información en tiempo real.

Componentes principales de Elasticsearch

A continuación se describen los componentes principales de Elasticsearch.
  • Índice: es una colección de documentos que comparten características similares. Cada índice puede contener múltiples tipos de documentos y cada tipo puede contener múltiples documentos. Por ejemplo, si creamos un índice llamado "Mi blog", podemos indexar documentos de tipo "post" o de tipo "comentario".
  • Documento: un documento se representa en formato JSON y se almacena en un índice. Cada documento tiene un tipo y un identificador único.
  • Mapeo: el mapeo se utiliza para realizar la correspondencia entre un campo de un documento JSON y su tipo de dato. Elasticsearch crea estos mapeos de forma automática al indexar un nuevo documento, aunque pueden ser asignados de forma manual también.
  • Shard: un shard es una entidad física donde se almacenan los datos para los distintos índices creados. Cada índice puede tener diversos shard, tanto primarios como secundarios (para respaldos, balanceo de carga, etc.). Los shard pueden estar distribuidos en los diferentes nodos que pueden componer un cluster Elasticsearch. Al iniciar una instancia de Elasticsearch (un nodo), se crea un cluster de nombre "elasticsearch" y se crean 5 shard primarios por cada índice.

Puesta en marcha de Elasticsearch

Después de instalar y configurar Java, deberemos descargar y descomprimir la distribución de Elasticsearch. Accederemos a la carpeta bin dentro del directorio donde hayamos descomprimido Elasticsearch y ejecutaremos el fichero elasticsearch.bat (en Windows).

Una vez iniciado, accederemos a la URL http://localhost:9200/?pretty con nuestro navegador para comprobar que funciona correctamente. Se mostrarán algunos datos básicos como el nombre del clúster y del nodo (como curiosidad, comentar que se asigna un nombre de nodo correspondiente a un personaje de Marvel...).



Ahora instalaremos el plugin kopf, para lo que abriremos una consola de comandos en el directorio bin de Elasticsearch y ejecutaremos el siguiente comando:


Una vez instalado, podremos acceder a la URL http://localhost:9200/_plugin/kopf donde veremos el cluster, los nodos, los shards y su estado.




Trabajando con documentos

Vamos ahora a indexar algunos documentos. A la hora de insertar un nuevo documento mediante la API REST, se sigue el patrón http://localhost:9200/<indice>/<tipo>/[<id>]. El índice se crea si no existe, así como el tipo. El identificador es opcional, y si no se proporciona Elasticsearch asignará uno de forma automática.

Por ejemplo, vamos a utilizar Elasticsearch para indexar nuestra biblioteca multimedia. Empezaremos por los libros, añadiendo un nuevo al índice llamado "multimedia". Realizaremos llamadas a la API mediante la extensión de Chrome Advanced Rest Client.

Indexaremos un nuevo libro realizando una petición PUT a la URL http://localhost:9200/multimedia/libro/1 enviando en el cuerpo del mensaje el siguiente documento JSON.


Podemos visualizar los índices contenidos en Elasticsearch para ver que se ha creado el nuevo índice correctamente, mediante la consulta GET http://localhost:9200/_cat/indices. Esta consulta devuelve un listado de índices en formato texto, donde veremos nuestro nuevo índice "multimedia".

Ahora vamos a realizar algunas búsquedas. Mediante la consulta GET http://localhost:9200/_search podemos visualizar todos los documentos indexados, para comprobar que nuestro libro se ha insertado correctamente.

Esta consulta de documentos, puede realizarse también en relación al índice o al tipo de documento. Por ejemplo, para obtener todos los libros se utilizaría la consulta siguiente http://localhost:9200/multimedia/libro/_search

Para realizar una búsqueda de un libro que contenga en cualquiera de sus campos un texto determinado, se puede utilizar la siguiente consulta de tipo POST http://localhost:9200/multimedia/_search En el cuerpo de la petición, enviaremos el término por el que queremos realizar la búsqueda. En este caso, vamos a buscar por la palabra "pampinoplas" enviando el siguiente JSON codificado mediante Query DSL en el cuerpo de la petición.



Si lo que queremos es buscar un libro por un campo determinado, se utilizaría la consulta POST http://localhost:9200/multimedia/_search enviando el siguiente JSON, donde se especifican los campos en los que realizar la búsqueda del término proporcionado.



Conclusiones

Elasticsearch es una excelente solución para la indexación de documentos, pensada para realizar búsquedas eficientes en grandes cantidades de datos. Es importante mencionar que Elasticsearch está concebida para realizar búsquedas rápidas, por lo que no soporta transacciones ni sus principios ACID.

El ciclo de vida de los datos suele ser de una única escritura en Elasticsearch y múltiples lecturas. Por ejemplo, puede utilizarse como soporte a bases de datos relacionales que sí soportan los principios ACID, pero que no disponen de sistemas de indexación de texto eficientes. De esta manera, se pueden replicar datos de forma asíncrona a una instancia de Elasticsearch para delegar en esta plataforma las búsquedas de texto.

Como ya he comentado, el poder de Elasticsearch aumenta en combinación con Logstash y Kibana, conformando el stack ELK. En siguientes entradas hablaré del resto de componentes de este stack y pondré algún ejemplo de uso.

Si necesitas más información sobre Elasticsearch, te recomiendo que te compres un par de libros muy completos, "Elasticsearch In Action" y "Elasticsearch: The Definitive Guide".

En esta entrada veremos la implementación del controlador y la fachada. El controlador gestionará las peticiones HTTP y hará uso de la clase fachada, que albergará la lógica de negocio de nuestro servicio. Esta última utilizará el repositorio de datos para realizar operaciones contra la base de datos.


Finalizaré la entrada con algunos ejemplos de consumo de la API implementada con un cliente REST y unas conclusiones sobre este primer tutorial básico de implementación de una API RESTful con Spring Framework.

Fachada

La clase fachada implementada es LibroFacade, y puesto que de momento nuestra lógica de negocio es muy sencilla, simplemente realiza llamadas al repositorio de datos. Hemos utilizado las siguientes anotaciones en la clase.
  • @Service (línea 12): utilizada con el fin de que Spring Framework sepa que es un componente y pueda gestionarlo como tal. 
  • @Autowired (línea 15): puesto que la clase hace uso del repositorio de datos (LibroJpaRepository), se ha realizado una inyección de dependencia mediante esta anotación. 
La clase dispone de métodos para:
  • Obtener todos los libros (línea 18). 
  • Obtener un libro por su identificador (línea 22).
  • Actualizar un libro (línea 26). 
  • Insertar un libro (línea 30).
  • Borrar un libro (línea 35). 

Controlador

La clase LibroController será el controlador que gestione las peticiones HTTP. Esta clase utiliza las siguientes anotaciones.
  • @RestController (línea 20): para informar a Spring de que la clase es un controlador. Quitar el nombre del controlador por ahora
  • @Autowired (línea 24): para la inyección de dependencia de la clase fachada.
  • @RequestMapping (p. ej. línea 37): indica la ruta que se deberá utilizar para llamar al servicio. Esta anotación se encuentra en la propia clase (ruta raíz del servicio /libros) y en los métodos para indicar las rutas concretas para esas peticiones respecto a la ruta raíz. Si la ruta se especifica mediante llaves, se indica que el valor será variable (por ejemplo, un GET a libros/1 o libros/2). Además se han utilizado las propiedades method y consumes para especificar el tipo de llamada HTTP que soportan y el tipo de dato que esperan consumir.
  • @RequestBody (p. ej. línea 38): indica la entidad a la que se mapearán los datos que lleguen en el cuerpo de la petición HTTP. En este caso se enviará un JSON representando al libro en cuestión. Para realizar el mapeo correctamente, el documento JSON deberá tener las mismas propiedades que la clase Libro.
  • @PathVariable (p. ej. línea 33): se utiliza para asignar a una variable el valor de la ruta especificado entre llaves. 
Las llamadas soportadas por el controlador son las siguientes:
  • GET /libros: obtiene todos los libros.
  • GET /libros/1: obtiene un libro por su identificador.
  • PUT /libros/1: actualiza un libro determinado.
  • POST /libros: crea un nuevo libro.
  • DELETE /libros/1: borra un libro por su identificador.
En el método de inserción de un libro (línea 44), se ha implementado un fragmento de código que incorpora en la cabecera de la respuesta el link al nuevo recurso creado (líneas 48 a 51). 


Consumo de la API

Para poder consumir nuestro servicio, podemos utilizar cualquier cliente REST. En este caso, vamos a utilizar Advanced Rest Client, que es una extensión de Chrome. En la siguiente animación, se muestra cómo se obtiene en primera instancia el listado de libros en formato JSON. Después se obtiene el libro número 1.

Posteriormente, se inserta un nuevo libro, proporcionando el JSON que representa el libro y especificando el tipo de dato que se envía en el cuerpo de la petición (Content-Type: application/json). Finalmente se elimina el libro insertado.



Los códigos de respuesta obtenidos son proporcionados de forma automática por el controlador de Spring Framework en base al tipo de operación que se realiza.

  • 200 - OK, es la respuesta estándar para las peticiones que se han servido correctamente.
  • 201 - Creado, se utiliza para informar de que un recurso se ha creado correctamente. 

Conclusiones

Y hasta aquí el ejemplo básico de implementación de una API RESTful con Spring Framework y JPA. Como se puede observar, Spring Framework simplifica mucho desarrollo de una API RESTful, evitando al programador la escritura de líneas redundantes que tienden a aumentar el número de errores. 

Además, los métodos básicos de gestión de recursos vienen ya proporcionados por Spring Framework mediante sus repositorios de datos. Estas características hacen de esta aproximación una buena elección de cara a implementar una API RESTful de forma sencilla, centrando las labores de programación, sobre todo, en la lógica de negocio del propio servicio.

En próximas entradas actualizaré el proyecto para incorporar nuevas funcionalidades con el fin de poder ver más características de Spring Framework y JPA, como por ejemplo, el control de excepciones, nuevas anotaciones, mapeos de relaciones entre entidades, etc.

Pulsa aquí para acceder al código de esta entrada en mi repositorio Github
En esta entrada veremos cómo implementar el acceso a base de datos. Para ello, utilizaremos los repositorios de datos que Spring nos ofrece, y que nos harán la vida mucho más fácil. Concretamente, utilizaremos el repositorio de datos denominado CrudRepository, que como su propio nombre indica, nos posibilitará la realización de operaciones CRUD contra nuestra base de datos. Existe otro tipo de repositorio más completo que veremos en siguientes entradas.



Para utilizar este tipo de repositorio de Spring Framework, simplemente tendremos que crear una interfaz que extienda de CrudRepository, tal y como hemos hecho al crear la interfaz de acceso a datos LibroJpaRepository. Debemos especificar también, la clase que representa la entidad que va a ser gestionada por el repositorio, en este caso Libro, y el tipo de dato utilizado para representar la clave primaria en dicha entidad, en este caso un Integer (línea 6). Y nada más!


Las operaciones básicas de tipo CRUD las proporciona el propio repositorio sin necesidad de programar absolutamente nada más. Los métodos que proporciona el repositorio son los siguientes.
  • save(Libro libro): guarda o actualiza un libro y devuelve la entidad con el identificador asignado.
  • findOne(Integer id): devuelve un libro determinado.
  • findAll(): devuelve un Iterable<Libro> con todos los libros de base de datos.
  • count(): devuelve un dato de tipo Long con el número de libros.
  • delete(Integer id): borra un libro determinado.
  • exists(Integer id): retorna un boolean que informa de la existencia de un determinado libro.
Cada método se engloba en una transacción de forma automática, por lo que tampoco tendremos que gestionar las transacciones en este caso. Menos es más!

En la siguiente entrada hablaremos de las clases que van a consumir este repositorio de datos. Por una parte el controlador, que será el encargado de atender las peticiones HTTP y servir los datos, y por otra parte la clase que hará las veces de fachada (patrón facade), encapsulando la lógica de negocio necesaria para atender las peticiones que llegan al controlador y realizar las consultas a base de datos pertinentes por mediación del repositorio de datos.
Pulsa aquí para acceder al código de esta entrada en mi repositorio Github
Es el turno de ver cómo podemos anotar nuestras entidades para que Hibernate pueda gestionarlas de forma adecuada con el fin de realizar los mapeos correspondientes con la base de datos.



En este caso, anotaremos la clase Libro, que se encuentra en el paquete spring.rest.jpa.i.bean. Recordemos que este paquete es el que se ha indicado en la configuración del entityManagerFactory del fichero applicationContext.xml, para que Spring pueda encontrar anotaciones como las siguientes.
  • @Entity (línea 10): indica que es una entidad que tendrá representación en base de datos.
  • @Table (línea 11): se utiliza para especificar la tabla a la que representa la clase, mediante la propiedad name. Esta anotación no es necesaria si la tabla tiene el mismo nombre que la clase.
  • @Id (línea 14): necesaria para identificar la propiedad de la clase que representa a la clave primaria de la tabla en base de datos.
  • @GeneratedValue (línea 15): se utiliza para especificar la estrategia de generación de la clave primaria. En este caso, se utiliza IDENTITY, que es la que hemos utilizado para configurar la estrategia de generación de claves primarias en la tabla libros. Mediante esta estrategia, HSQL genera un identificador de forma automática cada vez que se inserta un nuevo libro. Existen otras estrategias de generación de claves primarias (más info.). 
  • @Column (líneas 18 y 22): esta anotación se utiliza para mapear las propiedades de la clase con las columnas en base de datos. Si la propiedad tiene un nombre diferente al de la columna, se usa la propiedad name para indicar el nombre, tal y como hemos hecho en la línea 22.


Pulsa aquí para acceder al código de esta entrada en mi repositorio Github
Seguimos con esta segunda entrada dedicada a la implementación de una API RESTful con Spring Framework. En esta ocasión, veremos los detalles de configuración.



A continuación, detallaremos los siguientes ficheros de configuración para finalizar con unas breves conclusiones:

applicationContext.xml

El fichero applicationContext.xml corresponde al contexto raíz de nuestro servicio. Es aquí donde se declaran las clases o beans de nivel general para que Spring pueda gestionarlos. Los beans configurados son los siguientes.
  • dataSource (línea 20): indica la fuente de datos, en este caso una base de datos embebida de tipo HSQL. 
  • jpaVendorAdapter (línea 25): necesario para indicar a Spring, que será Hibernate el framework que utilizaremos como proveedor JPA.
  • entityManagerFactory (línea 30): será el objeto que Spring utilizará para interactuar con la base de datos. Hay dos propiedades fundamentales.
    • packagesToScan: indica el paquete donde se encuentran los beans que representan las entidades de nuestro servicio, en este caso, libros. Como veremos más adelante, estas entidades estarán anotadas para que Hibernate pueda relacionarlas con su correspondiente representación en base de datos.
    • jpaProperties: aquí se especifican las propiedades de Hibernate. En esta ocasión, se ha especificado el dialecto de base de datos a utilizar y el esquema por defecto, para que Hibernate cree las consultas SQL de forma adecuada.
  • transactionManager (línea 44): declaración del tipo de gestor transaccional, en este caso JPA.
  • databaseManager (línea 49): posibilita el uso de un gestor de base de datos HSQL. Este bean es opcional, pero el gestor permitirá modificar los datos fácilmente para la realización de pruebas.

dispatcher-servlet.xml

El fichero dispatcher-servlet.xml contiene la declaración de aquellos beans que están directamente relacionados con el servicio a implementar. A partir de esta configuración, Spring creará un Servlet que gestionará las peticiones que se realicen a la API, redireccionándolas a los controladores correspondientes. 

En lugar de declarar los beans en el fichero XML de forma explícita, se ha optado por una configuración mediante anotaciones en las propias clases Java. Para ello, se han declarado las etiquetas necesarias para que Spring pueda detectar en qué clases se encuentran estas anotaciones.
  • En la línea 18 se especifica el paquete raíz a partir del cual Spring buscará beans de nuestra lógica de negocio (servicios, componentes, etc.)
  • En la línea 21 se especifica el paquete donde están los repositorios JPA que se utilizarán para acceder a base de datos,
  • En la línea 24 se configuran las transacciones mediante anotaciones
  • En la línea 26 se especifica que los controladores que procesarán las peticiones estarán también anotados.
Existen dos contextos de aplicación: un contexto raíz (applicationContext) y otro hijo (dispatcher-servlet). Los beans definidos en el contexto hijo tienen acceso a los definidos en el padre, pero no al revés. Es por ello que en el dispatcher-servlet se encuentran los componentes específicos del servicio web (p.ej. controllers), y en el applicationContext se encuentran los componentes generales (p.ej. acceso a datos).

web.xml

En el fichero web.xml se declara el dispacher-servlet para su inicialización al desplegar el servicio web en el servidor, indicando el fichero en el que se encuentra su configuración (línea 12). En las líneas 20 y 23 se especifica un listener necesario para que Spring inicialice el contexto de aplicación y la ubicación del fichero con su configuración.

Conclusiones 

En la versión actual de Spring Framework, la posibilidad de anotar las propias clases Java simplifica mucho la configuración de nuestro servicio. En siguientes entradas, veremos cómo se deben realizar las anotaciones de las entidades, controladores, etc.
Pulsa aquí para acceder al código de esta entrada en mi repositorio Github
Estreno blog con una serie de entradas en las que veremos cómo implementar una API RESTful sobre plataforma Java utilizando diversos módulos del siempre maravilloso Spring. Concretamente, utilizaremos el módulo principal Spring Framework y el módulo Spring Data. De este último, usaremos las funcionalidades para acceso a datos mediante JPA (Java Persistence API), utilizando Hibernate como proveedor JPA.

Para ponernos en situación, vamos a suponer que queremos implementar una API que nos permita gestionar todos los libros que tenemos en nuestra biblioteca personal. En esta primera entrada veremos cómo definir algunos servicios básicos de tipo CRUD (Create Read Update Delete) para una gestión inicial de los libros.

Las herramientas necesarias para seguir esta serie de entradas son las siguientes:
Concretamente, en esta entrada trataremos los siguientes puntos de cara a la implementación del servicio, que iremos completando en siguientes entradas.

Definición de los servicios

Empezaremos definiendo los servicios REST para una gestión básica de los libros. Para ello, contaremos con los siguientes servicios:
  • GET /libros: obtener una lista de todos los libros de la biblioteca.
  • GET /libros/1: obtener un libro concreto por su identificador.
  • POST /libros: crear un nuevo libro.
  • PUT /libros/1: actualizar un libro concreto por su identificador.
  • DELETE /libros/1: borrar un libro concreto por su identificador.
Es recomendable que los nombres (nunca verbos) que utilicemos para identificar los servicios estén en su forma plural en base al recurso gestionado, tal y como hemos especificado con nuestros libros. Existen multitud de buenas prácticas para la implementación de servicios REST. En este y este enlace podréis encontrar multitud de buenos consejos.

Definición de la base de datos

A continuación, definiremos la base de datos para almacenar los libros. Inicialmente, contaremos con un identificador, el nombre y el año de publicación de cada libro. El siguiente esquema, corresponde a la creación de la tabla para almacenar las propiedades mencionadas de cada libro. Este esquema se basa en la sintaxis de la base de datos HSQLDB, la cual usaremos en su modo embebido para este ejemplo.

En la línea 2 podemos observar la creación de la clave primaria mediante la instrucción IDENTITY, la cual creará una clave auto-incremental en base de datos de forma automática.
Además, hemos creado un script para insertar nuestros primeros libros de forma automática cuando el servicio se despliegue.


Estructura del proyecto

El proyecto está soportado por Maven. En el enlace que se encuentra al final del post encontrarás el acceso a mi repositorio Github para que puedas descargar el código del proyecto y empieces a trastear con él. La estructura del proyecto es la que se muestra a continuación.


En esta estructura, podemos encontrar diferentes paquetes con clases Java.
  • bean: paquete donde se encuentran las entidades Java que representan los recursos, en este caso libros.
  • controller: aquí se encuentran los controladores que se encargarán de procesar las peticiones que lleguen para gestionar los libros (GET, POST, PUT y DELETE)
  • facade: en este paquete se encuentran las clases donde se implementa la lógica de negocio, siguiendo el patrón facade
  • repository: aquí se encuentran las clases que implementarán el acceso a la base de datos.
Además, encontramos los esquemas de creación de la base de datos e inserción de datos en la carpeta db. También tenemos los ficheros de configuración de Spring (applicationContext.xml y dispacherServlet.xml) en la carpeta del mismo nombre, así como el fichero de Maven pom.xml con las dependencias y plugins necesarios para el despliegue del servicio.

Despliegue del servicio 

El servicio implementado se despliega en un servidor Tomcat que ya se incluye embebido en el proyecto. Para ello, hay que ejecutar el comando Maven siguiente: clean tomcat7:run
Esto iniciará el servicio, creando de forma automática la base de datos e insertando los datos que se encuentran en el script db/insert.sql. Verás que también se abre una ventana con el manager de gestión de datos para la base de datos embebida HSQL. Desde aquí podremos consultar y realizar modificaciones de nuestros datos directamente.

Para interactuar con el servicio, podemos acceder a la URL http://localhost:8080/spring.rest.jpa.i/libros mediante el cliente Advanced Rest Client o mediante un navegador web. Esta petición nos devolverá un listado de libros en formato JSON.


Conclusiones

En siguientes entradas explicaré cómo hemos implementado las diferentes clases (beans, controllers, etc.) y la configuración de Spring, además de completar esta API con nuevos servicios, más entidades, nuevas relaciones entre entidades, etc. También, veremos otros temas relacionados con el diseño de la API, como el versionado, la seguridad, cacheo, serialización avanzada, etc. De esta forma iremos tratando diferentes configuraciones y funcionalidades que nos ofrece Spring Data JPA para hacernos la vida más fácil y placentera.

Pulsa aquí para acceder al código de esta entrada en mi repositorio Github