maven

Proyecto en Eclipse con Jersey(JAX-RS 2.0)+Tomcat+JSON

En los últimos años se pretende llevar más allá la separación entre datos y representación: se está pasando de un modelo en el que la separación se hace en el servidor (con desarrollos MVC) a transferir la responsabilidad de la vista al cliente, con las nuevas “single-page web application” basadas en HTML5.

De este modo se pretende que la comunicación que se lleva a cabo entre el cliente y el servidor sea la mínima posible, siendo datos la información que se transfiere. Los más usados últimamente son los datos en formato JSON, que se adaptan perfectamente a los clientes hechos en JavaScript. Un paradigma de comunicación puede ser por lo tanto un servidor RESTful, tal y como vimos en el ejemplo de AngularJS y CouchDB (que implementa de forma nativa un servidor Web con comunicación RESTful para dar acceso a los datos).

Hay en el mercado diferentes opciones para crear un servidor RESTful con diferentes tecnologías. Al fin y al cabo se trata de implementar un servidor en el puerto 80 (normalmente) que responde al protocolo HTTP y a sus diferentes comandos (GET, POST, HEAD…). Podríamos implementar nosotros mismos uno cualquiera en cualquier lenguaje. La comunicación se realiza a través de diferentes protcolos, como XML, JSON, Texto plano… No pienses que RESTful se emplea para desarrollar aplicaciones Web: también es un buen método para desarrollar la interoperabilidad entre diferentes sistemas.

Dentro de la oferta de Java, existen diferentes opciones, como por ejemplo usar un MVC como Struts2 y añadirle un plug-in para RESTful. Este plugin se encargará de traducir las peticiones HTTP y sus cabeceras a las diferentes acciones que hayamos desarrollado. También existe la opción de usar JAX-RS, que desde Java6 forma parte del estándar J2EE.

JAX-RS es un framework que nos permite desarrollar de una manera sencilla y fiable servicios web de tipo RESTful. Su implementación de referencia es el framework Jersey. Así pues, en este artículo vamos a ver cómo podemos montar en Eclipse un proyecto de base que funcione sobre Tomcat y además la comunicación se lleve a cabo a través de objetos JSON.

Antes de comenzar, hay que destacar que podemos apoyarnos en Maven para todo el proceso. Recomiendo el uso del plugin m2e que ya tratamos en otro artículo del blog (https://mysticalpotato.wordpress.com/2014/01/21/maven-en-eclipse-m2e/).

1. Crear Proyecto Web dinámico con Eclipse y Tomcat 7

Esto no debería ser problema. Como siempre, ir a File->New->Dynamic Web Project

newDynamicProject

Si además en el último paso indicamos que queremos crear el fichero web.xml mucho mejor (aunque posteriormente vamos a sobreescribirlo).

2. Incluir las bibliotecas de Jersey y Jackson

Ahora dos alternativas, dependiendo de si usamos o no Maven:

Si NO usamos Maven

a. Ir a https://jersey.java.net/download.html y descargar el fichero “Jersey JAX-RS 2.0 RI bundle” y descomprimirlo en nuestro disco duro.
b. Copiar las bibliotecas de los tres directorios a WEB-INF/lib (o incluirlas en el proyecto, si es así, tener en cuenta que hay que añadirlas en el deploy).

También debemos instalar Jackson, que es el parser para transformar a JSON las comunicaciones del servidor RESTFUL:

a. Ir a http://wiki.fasterxml.com/JacksonDownload y descargar la última versión
b. Copiar las bibliotecas a WEB-INF/lib (o incluirlas en el proyecto, si es así, tener en cuenta que hay que añadirlas en el deploy).

En el caso de que estemos usando Maven

En primer lugar podemos convertir un proyecto que no tengan Maven en uno que sí lo tenga. En Eclipse con m2e es muy sencillo: pulsamos con el botón auxiliar del ratón en el proyecto, luego en Configure->Convert to Maven Projects

jax1

Una vez tenemos el proyecto convertido a Maven podemos incluir las dependencias en el fichero pom.xml; se puede hacer tanto con la interfaz gráfica de Eclipse como editando el código. Habría que incluir las siguientes:


<dependencies>
 <dependency>
 <groupId>org.glassfish.jersey.containers</groupId>
 <artifactId>jersey-container-servlet</artifactId>
 <version>2.5.1</version>
 </dependency>
 <dependency>
 <groupId>org.glassfish.jersey.core</groupId>
 <artifactId>jersey-client</artifactId>
 <version>2.5.1</version>
 </dependency>
 <dependency>
 <groupId>com.fasterxml.jackson.jaxrs</groupId>
 <artifactId>jackson-jaxrs-json-provider</artifactId>
 <version>2.2.3</version>
 </dependency>
 </dependencies>
 

Las dos primeras dependencias indican que se va a emplearn Jersey versión 2.5.1 (la última cuando se creó este post). El siguiente, com.fasterxml.jackson.jaxrs, es provider de JSON para poder transformar la comunicación RESTful a este protocolo.

3. Crear el web.xml para que la aplicación funcione correctamente en nuestro contenedor de servlets (Tomcat 7 en nuestro caso)

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
 xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
 id="WebApp_ID" version="3.0">
 <display-name>js6</display-name>

<servlet>
 <servlet-name>JAX-RS Servlet</servlet-name>
 <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
 <init-param>
 <param-name>jersey.config.server.provider.packages</param-name>
 <param-value>com.mysticalpotato.JAX</param-value>
 </init-param>
 <load-on-startup>1</load-on-startup>
 </servlet>
 <servlet-mapping>
 <servlet-name>JAX-RS Servlet</servlet-name>
 <url-pattern>/rest/*</url-pattern>
 </servlet-mapping>

<welcome-file-list>
 <welcome-file>index.html</welcome-file>
 <welcome-file>index.htm</welcome-file>
 <welcome-file>index.jsp</welcome-file>
 <welcome-file>default.html</welcome-file>
 <welcome-file>default.htm</welcome-file>
 <welcome-file>default.jsp</welcome-file>
 </welcome-file-list>
</web-app>

¿Qué es importante en este web.xml?

Principalmente que existe un servlet (org.glassfish.jersey.servlet.ServletContainer, solo en el caso de JAX-RS 2) que se encarga de capturar todas las peticiones que le lleguen al subdirectorio “rest” del servidor y que las procesará como RESTful: tendrá en cuenta la URL y las operaciones de la cabecera y las asignará a determinadas clases. El directorio “rest” puede ser cambiado por el que nosotros indiquemos.

Además hay otro parámetro muy interesante, jersey.config.server.provider.packages, que indica en qué paquete están las clases a las que se tiene que mapear las peticiones RESTful. No existe un fichero de asignación como tal, sino que, a través de anotaciones, en cada clase se indica la URL a la que responde cada una, y dentro de ella, qué métodos responden a cada tipo de operaciones y qué resultado o entrada procesan. En el caso del ejemplo está asignado a “com.mysticalpotato.JAX” pero puedes cambiarlo por lo que creas conveniente.

4. Crear una aplicación básica

Una vez tenemos configuradas las bibliotecas y el contenedor de servlets, podemos comenzar a desarrollar nuestro primer servicio:

En primer lugar vamos a crear una sencilla clase de datos, un POJO que contiene tres características de un coche. Lo albergamos en el paquete com.mysticalpotato.data:

package com.mysticalpotato.data;

public class Coche {

 String marca;
 String modelo;
 long potencia;
 public String getMarca() {
 return marca;
 }
 public void setMarca(String marca) {
 this.marca = marca;
 }
 public String getModelo() {
 return modelo;
 }
 public void setModelo(String modelo) {
 this.modelo = modelo;
 }
 public long getPotencia() {
 return potencia;
 }
 public void setPotencia(long potencia) {
 this.potencia = potencia;
 }
}

De este clase implementaremos un objeto que será el que se transforme de modo automático a formato JSON cuando se publique el punto de acceso RESTful;

Creamos un paquete para albergar los servicios RESTful, que coincida con lo indicado en el fichero web.xml como parámetro del servlet RESTful. En el caso de este ejemplo tenemos com.mysticalpotato.JAX.

Dentro vamos a crear la clase “Coches” con el siguiente código:

package com.mysticalpotato.JAX;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

import com.mysticalpotato.data.Coche;

@Path("coches")
public class Coches {

@GET
 @Produces(MediaType.APPLICATION_JSON)
 public Coche getIt() {
 Coche c = new Coche();
 c.setMarca("Ferrari");
 c.setModelo("F45");
 c.setPotencia(430);
 return c;
 }
}

Cosas importantes:

  • Se ha declarado “coches” como la URL de acceso a este punto RESTful. Entonces la URL será del tipo http://servidor.com/rest/coches. El “directorio” rest viene de la confijguración del servlet en el web.xml.
  • Cuando se haga una petición de tipo GET (la que se hace cuando se pide una página en un navegador), se accederá al método getIt(). La razón es que hemos indicado esto con la anotación @GET.
  • El tipo de datos de salida será de tipo JSON, y funcionará además porque hemos usado el proveedor Jackson, que se encargará de hacerlo funcionar.

La salida será:

{"marca":"Ferrari","modelo":"F45","potencia":430}

Un aspecto importante: será necesario indica que cuando el proyecto se despliegue en el servidor, también lo hagan las librerías de Maven que se han indicado. Para ello ir a propiedades del proyecto->Deployment Assembly y añadir las dependencias de Maven o de los ficheros si se ha hecho en manual.

Maven en Eclipse: m2e

Una de las cosas que menos me gusta a la hora de empezar un proyecto nuevo es tener que comenzar desde cero a montar todas la infraestructura para poder comenzar. A pesar de que Eclipse tiene diversas plantillas, éstas son muy genéricas y hay que complementarlas con librerías y servidores, lo que nos puede llevar un buen rato hasta que comenzamos a poder empezar a ser productivos. Por ejemplo si queremos un proyecto que lleve struts2 con acceso JPA a base de datos, tenemos que ir a proyecto java web y complementar las librerías.

El punto ideal de comienzo de un proyecto sería disponer de un “Hola Mundo” básico sobre el cual empezar a programar, saltándonos la configuración previa. Afortunadamente en el mundo Java existe una herramienta llamada Maven que facilita el ciclo de vida de una aplicación, desde su creación (aspecto del que va este post) hasta el testing o el deploy. En este post solamente vamos a ver cómo aprovechar una pequeña parte de su potencial para facilitarlos el inicio de un proyecto.

Maven es una herramienta de Apache que puede ser descargada en http://maven.apache.org/ y que se puede encontrar una introducción a su uso en http://maven.apache.org/users/index.html (recomiendo el apartado http://maven.apache.org/guides/getting-started/maven-in-five-minutes.html para un rápido vistazo práctico a su potencial). Su funcionamiento para la creación está basado en los “Archetypes” que son una especie de plantillas que utilizaremos para crear nuestro proyectos y que contiene una descripción de las librerías y otras dependencias, así como unos ficheros básicos que contiene el “hola mundo” que necesitamos para comenzar.

Mediante Maven podemos “instanciar” un archetype de nuestro gusto de los múltiples que hay por Internet, como por ejemplo de http://search.maven.org/ o de http://mvnrepository.com/ . Hay cientos de ellos asi que es complicado no encontrar uno que se adapte a nuestras necesidades. Maven descarga el archetype y crea en el directorio que le indiquemos una implementación del proyecto. Además se descarga las bibliotecas .jar en un lugar común en nuestro ordenador y establece las dependencias para que simplemente ejecutemos.

Como se trata de una herramienta básica que muchos otros programas utilizan y además es susceptible de automatizar (sobre todo por las otras etapas del ciclo de vida en las que es de gran ayuda), no tiene una interfaz gráfica. Esto tiene sus ventajas e inconvenientes. En este post vamos a ver cómo la podemos integrar dentro de Maven para que los usuarios que están más acostumbrados a usar eclipse, puedan usar las ventajas de esta herramienta. Para ello existe el plugin m2e para Eclipse, así como su integración en WTP (Web Tools Platform) de Eclipse. Este plugin ya incorpora una edición embebida de Maven para que no tengamos que hacer nada

Pasos para ponerlo en práctica:

1. Instalar los plugins de eclipse m2e y m2e-wtp, que integra maven con el WTP (Web Tools Platform) de eclipse:

  • Ir a Help->Install new Software e introducir la siguiente URL: http://download.eclipse.org/m2e-wtp/releases/juno/ (o la distribución de Eclipse que estemos usando – Kepler, Galileo…-)
  • Elegir el plugin m2e y el m2e-wtp normal (no el SDK, aunque podemos instalarlo si queremos)
  • Reiniciar Eclipse para finalizar la instalación

(En la página oficial de los plugins hay un excelente video de poco más de un minuto http://www.eclipse.org/m2e-wtp/)

2. Para crear un repositorio:

  • Crear un proyecto nuevo (File->New->Other o Ctrl+N)
  • En la caja de elección del wizard, escribir Maven para localizar los proyectos de Maven
  • Elegir “Maven Project”
  • Usar el default workspace de Eclipse (para que cree el proyecto en nuestro workspace, que suele ser lo más habitual).
  • En la ventana de elección del archetype, pinchar en Configure, puesto que no están los que buscamos (necesitamos localizar un Archetype de Struts2).
  • En la ventana de configuración de Archetypes, pulsar sobre “Add Remote Catalog” y añadir un nuevo repositorio con esta URL: http://repo1.maven.org/maven2/archetype-catalog.xml
  • Elegir como fuente el nuevo catálogo (yo lo he llamado “Maven General” pero puedes llamarlo como quieras)
  • Pinchar sobre struts2-archetype-blank (que es un archetype de un proyecto struts2 en blanco. Puedes elegir el que quieras).
  • En la siguiente ventana, completar los datos del proyecto que amos a crear: Group ID para el grupo al que pertenece el proyecto; Artifact ID para designar el proyecto y Package para nombrar el paquete base que usaremos.
  • Pulsamos en finalizar para acabar con el wizard de creación.

Ahora esperamos unos instantes para que Maven descargue todas las librerías y dependencias que están declaradas en el archetype, y para que cree los ficheros básicos (algunas acciones de Struts2, JSP, los xml de configuración de la aplicación Web…)

Al final tendremos un proyecto basado en el Archetype elegido. Podremos ver que:

  • El proyecto se ha creado en nuestro workspace y que ya tiene una estructura completa creada, con las dependencias de struts2 necesarias, además de una aplicación de ejemplo con acciones básicas y un struts.xml
  • Las dependencias (los .jar) ahora se encuentran en nuestro ordenador en nuestro directorio personal de nuestro sistema operativo /.m2/repository
  • Podemos tratar el proyecto como si de un proyecto web de Eclipse se tratara. Esto es posible gracias al plugin m2e-wtp.

Desafortunadamente puede surgir una serie de problemas derivados del uso del plugin m2e para Eclipse, que podremos resolver con ayuda de StackOverflow en su mayor parte. Aquí van alguno de los problemas que he experimentado, aunque todos ellos se han podido resolver con no mucho trabajo:

  • El problema persistente del fichero Missing artifact com.sun:tools:jar:1.5.0:system pom.xml que es un fichero que lleva el JRE pero que no es capaz de encontrar. Esta biblioteca (tools.jar) forma parte de la instalación del JDK pero no del JRE. Si tenemos el JRE instalado y eclipse está corriendo con la máquina virtual del JRE y no del JDK puede sucedernos este error, puesto que no es capac de encontrar la biblioteca tools.jar. Se soluciona de esta forma:
    •  editando el fichero eclipse.ini e incluyendo justo antes de de -vmargs la sentencia: vm “C:\Program Files\Java\jdk1.6.0_37\bin\javaw.exe”. (o donde se tenga el JDK instalado.
    •  usando el JDK como entorno de ejecución del proyecto (y no el JRE).
    •  por último haciendo un clean del proyecto seguido de un build para que coja los cambios.
  •  Descarga incorrecta de bibliotecas. Es algo que en Maven en modo comando es fácil de detectar puesto que avisa de que el hash del fichero no coincide con el proporcionado, pero en Eclipse debemos esperar a un build del proyecto. De este modo recibiremos errores de clases no encontradas o similar. La razón es que el fichero .jar está corrupto y no puede extraerse la información. La solución:
    • Ir al repositorio de bibliotecas que hay en nuestro perfil de usuario (en mi caso: C:\Users\mysticalpotato\.m2\repository) y localizar el .jar. Se renombrar o elimina y se actualiza el proyecto con Maven (botón auxiliar sobre el proyecto, Maven->Update Project) para que se vuelva a bajar todas las bibliotecas que no están. Si hay suerte se bajarán las correctas, sino habrá que insistir