martes, 3 de febrero de 2015

SLF4J - Log4J - Jax-Rs - Glassfish

Simple Logging Facade for Java (SLF4J) sirve como una fachada o abstracción para el framework de logueo utilizado. (java.util.logging, logback, log4j) permitiendo al usuario final conectar el framework deseado en tiempo de despliegue.

Log4j, es una librería para logueo en Java.

En este post veremos como habilitar el logueo en aplicaciones REST desplegadas en Glassfish 4.0 utilizando SLF4J y el framework Log4J. Pasos:

incluir las dependencias

Incluir la dependencia para SLF4J.

dependencies {
 providedCompile ( 
  ['javax.ws.rs:javax.ws.rs-api:2.0.1'],
  ['org.glassfish.jersey.containers:jersey-container-servlet:2.13'],
  ['com.sun.jersey:jersey-server:1.18.1'],
  ['org.eclipse.persistence:org.eclipse.persistence.jpa:2.5.2']
 )
 
 compile(['org.slf4j:slf4j-log4j12:1.7.10'])
    
        runtime files(['libs/ojdbc7.jar'],['libs/ojdbc14.jar'])
    
 testCompile 'junit:junit:4.11', 'org.mockito:mockito-core:1.10.8'
}

La dependencia antes descrita descarga tanto SLF4J como Log4J.

Agregar archivo lo4j.properties a glassfish

  • Crear log4j.properties
  • Crear el archivo log4j.properties con el siguiente contenido:

    # Root logger option
    log4j.rootLogger=DEBUG, file
     
    # Redireccionar el log de mensajes a un archivo
    log4j.appender.file=org.apache.log4j.RollingFileAppender
    log4j.appender.file.File=${com.sun.aas.instanceRoot}/logs/archivo.log
    log4j.appender.file.Append = true
    log4j.appender.file.MaxFileSize=5MB
    log4j.appender.file.MaxBackupIndex=10
    log4j.appender.file.layout=org.apache.log4j.PatternLayout
    log4j.appender.file.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
    

    Colocar el archivo antes creado en /Applications/glassfish4/glassfish/domains/domain1/config

  • Agregar log4j.jar
  • colocar el jar log4j en el directorio glassfish/lib de glassfish

  • Agregar opción JVM para logueo log4j
  • Agregar la siguiente opción de la máquina virtual de java al servidor

    -Dlog4j.configuration=file:///${com.sun.aas.instanceRoot}/config/log4j.properties

    Para realizarlo abrir la consola de glassfish —> Configurations —> server-config —> JVM Settings —> Jvm options

    Click en save

    Restart glassfish

Utilizar sentencias proporcionadas por el API

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Implementación del consumo del catálogo x obteniendo la información
 * desde x repositorio.
 * 
 * @author Clemente Morales Fernández
 * @since Jan 12, 2025
 *
 */
public class CatalogoImpl implements CatalogoSexo {
    /**
     * Logueo de eventos de la aplicación.
     */
    final Logger log = LoggerFactory.getLogger(CatalogoImpl.class);
    
    @Override
    public List getSexos() {
        String catalogo = getCatalogoDesdeRepositorio();
        log.info(String.format("Catalogo obtenido desde repositorio x. %s",
                catalogo));
        return CatalogoConverter.toTransferObject(catalogo);
    }

    /**
     * Permite obtener el catálogo desde el repositorio.
     * 
     * @return Catálogo ...
     */
    private String getCatalogoDesdeRepositorio() {

        String catalogo = "";
        try {
            
            catalogo = obtenerCatalogo();
        } catch (Exception exception) {
            log.error("Error obteniendo el catálogo x. ", exception);
        }
        
        return catalogo;
    }
}

jueves, 30 de octubre de 2014

JAX-RS + Jersey-Filtros e interceptores

Filtros e interceptores

Filtros e interceptores son objetos que pueden ser utilizados en el procesamiento de solicitudes o respuestas del servidor.

Estos objetos permiten encapsular comportamiento relacionado con el procesamiento de aspectos del protocolo o infraestructura de la aplicación y que no deseamos manejar en el modelo de negocios.

Los filtros e interceptores pueden ser utilizados tanto del lado del cliente como del lado del servidor.

Filtros

Los filtros pueden modificar solicitudes y respuestas al servidor, incluyendo los headers y otros parametros de request/response.

Interceptores

Los interceptores son utilizados para modificar el contenido del mensaje, tanto de entrada como de salida. Por ejemplo comprimir y descomprimir o encriptar y desencriptar mensajes.

Configuración del orden de ejecución de filtros e interceptores

Si tenemos más de un filtro o interceptor, debemos definir el orden en el que deseamos que se ejecuten. En JAX-RS el orden de ejecución se puede fijar por medio de la anotación @Priority y en tiempo de ejecución el orden será determinado por medio del valor asignado, ejecutando primero los números más pequeños.

Si no se configura, los filtros e interceptores aplican a todas las solicitudes y respuestas del servidor, sin embargo se puede aplicar a métodos de recursos seleccionados por medio del registro de una implementación de la interfaz DinamicFeature o por medio de anotaciones. Para más información consultar la sección dynamic binding y name binding de la página oficial de Jersey.

Descomprimir/Comprimir solicitudes y respuestas del servidor

Vamos a crear un interceptor para comprimir y descomprimir los mensajes

Primero veremos como descomprimir el contenido del mensaje, para que cuando se ejecute el recurso deseado, los datos vayan en su formato no comprimido. Vamos a crear la clase CompresionReaderInterceptor con el siguiente código:

@Provider
public class CompresionReaderInterceptor implements ReaderInterceptor {
 @Override
 public Object aroundReadFrom(ReaderInterceptorContext context)
   throws IOException, WebApplicationException {
  String encoding = context.getHeaders().getFirst(CompresionWriterInterceptor.CONTENT_ENCODING_HEADER);
  
  if (!requestHasContentEncodingHeader(encoding)) {
   return context.proceed();
  }
  
  GZIPInputStream gzipInputStream = new GZIPInputStream(context.getInputStream());
  context.setInputStream(gzipInputStream);  
  return context.proceed();
 }

 private boolean requestHasContentEncodingHeader(String encoding) {
  return CompresionWriterInterceptor.CONTENT_ENCODING_HEADER_VALUE
    .equalsIgnoreCase(encoding);
 }
}

Ahora vamos a crear la clase CompresionWriterInterceptor para comprimir el contenido del mensaje una vez que se ha procesado el recurso solicitado.

@Provider
public class CompresionWriterInterceptor implements WriterInterceptor {
 public static final String CONTENT_ENCODING_HEADER_VALUE = "gzip";
 public static final String CONTENT_ENCODING_HEADER = "Content-Encoding";

 @Override
 public void aroundWriteTo(WriterInterceptorContext context)
   throws IOException, WebApplicationException {
  GZIPOutputStream gzipOuputStream = new GZIPOutputStream(
    context.getOutputStream());
  context.getHeaders().putSingle(CONTENT_ENCODING_HEADER,
    CONTENT_ENCODING_HEADER_VALUE);
  context.setOutputStream(gzipOuputStream);
  context.proceed();
  return;
 }
}

La anotación @Provider es la que permite que se detecte la clase como un elemento de interés para el servicio. Esta clase también puede ser registrada en la implementación de Application.

Aplicando filtros por medio de anotaciones

Como se mencionó antes si no se configura, los filtros e interceptores aplican a todos los request y responses del servicio. Vamos a ver como aplicarlo a solo un método de un recurso.

Necesitamos crear la siguiente anotación:

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

import javax.ws.rs.NameBinding;

/**
 * Permite especificar los recursos a los que se requiere aplicar un algoritmo
 * para comprimir y descomprimir mensajes.
 * 
 * @author Clemente Morales Fernández
 * @since Oct 30, 2014
 *
 */
@NameBinding
@Retention(RetentionPolicy.RUNTIME)
public @interface Compress {
}

Una vez creada la anotación lo que haremos será aplicarla en el método del recurso que deseamos aplicar compresión en la respuesta y al interceptor encargado de realizar la compresión.

@Compress
public class CompresionWriterInterceptor implements WriterInterceptor {
}

/**
 * Proporciona las operaciones disponibles para el recurso seguro.
 * 
 * @author Clemente Morales Fernández
 *
 */
@Path("seguros")
public interface SegurosService {
 /**
  * Permite obtener los seguros dados de alta por un cliente.
  * @param idCliente Identificador del cliente.
  * @return Lista de seguros del cliente.
  */
 @Path("/{idCliente}")
    @GET
    @Produces("application/json")
 @Compress
    List getSegurosCliente(@PathParam("idCliente") int idCliente);
}

En la siguiente entrada veremos como crear y configurar filtros e interceptores del lado del cliente.

viernes, 3 de octubre de 2014

Consumiendo recurso JSON con Jersey + Jackson + Gradle

Esta semana empecé a trabajar con la herramienta de construcción Gradle y en esta ocasión quiero conmpartir mi experiencia con la herramienta gradle, más la deserialización de la respuesta Json a un modelo de objetos. Para consultar la configuración del cliente para el consumo del recurso REST ir a la entrada anterior.

Instalando gradle

Vamos a empezar instalando gradle. Para esto les comparto la liga que he utilizado para hacerlo en mi entorno de desarrollo. Una vez terminado el proceso verificamos nuestra versión instalada por medio del comando:

gradle -version

Creando la estructura del proyecto

Primero que nada Gradle espera que el código de nuestra aplicación se encuentre en la estructura src/main/java y el código de nuestras pruebas en src/test/java.

Además cualquier archivo colocado bajo src/main/resources será incluido en el archivo JAR como recurso y cualquie archivo colocado sobre src/test/resources será incluido en el classpath utilizado para correr las pruebas unitarias.

Todos los archivos de salida son colocados en el directorio de construción build/libs.

Bueno para empezar vamos a crear la siguiente estructura de directorios:

-- JerseyConsumer
  -- src
    -- main
   -- java
     -- mx
    -- org
      -- mi empresa
   -- resources
    -- test
   -- java
   -- resources

Listo ya tenemos la estructura requerida por gradle para la aplicación.

Creando el archivo de construcción gradle

Necesitamos ahora indicar la configuración de construcción para nuestra aplicación. Para eso vamos a crear los siguientes dos archivos en el directorio JerseyConsumer.

Archivos configuración gradle

Vamos a abrir el archivo build.gradle con nuestro editor favorito y agregamos el siguiente bloque de código:

apply plugin: 'java'

repositories {
 mavenCentral()
}

dependencies {
 compile group:'org.glassfish.jersey.core', name:'jersey-client', version:'2.11'
        compile group:'org.glassfish.jersey.media', name:'jersey-media-json-jackson', version:'2.11'
 compile group:'org.apache.commons', name:'commons-lang3', version:'3.1'
}

task execute(type:JavaExec) {
   main = 'mx.org.miempresa.RestConsumer'
   classpath = sourceSets.main.runtimeClasspath
}

Algunas notas importantes el archivo de configuración:

  • apply plugin. Indica que trabajaremos con el plugin de java para gradle. Viene con la instalación por default de gradle.
  • repositories. Indicamos que utilizaremos el repositorio central de dependencias de maven. Es una de las razones por las que me gusta gradle, que permite utilizar varios repositorios de dependencias.
  • dependencies. Indica las dependencias de la aplicación. En este caso jersey-client (consumo recursos), jackson (deserialización de json response), commons-lang3 (utilerías para el API java.lang)
  • task execute. En gradle podemos utilizar las tareas proporcionadas por el pluging o crear nuevas extendiendo las existentes. En este caso extendemos la tarea JavaExec para pasarle el parámetro main (clase con el método main) y el classpath donde encontrará las dependencias.

Vamos a abrir el archivo gradle.properties con nuestro editor favorito y agregamos la siguiente configuración:

org.gradle.java.home=C:\\Program Files (x86)\\Java\\jdk1.7.0.67

En este archivo indicamos el jdk de java a utilizar para compilar nuestra aplicación.

Consumiendo el recurso y deserializando JSON a objetos:

Vamos a crear la clase RestConsumer en el paquete mx.org.miempresa

package mx.org.miempresa;

import java.util.List;

import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Invocation;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.GenericType;
import javax.ws.rs.core.MediaType;

import org.glassfish.jersey.client.ClientConfig;
import org.glassfish.jersey.jackson.JacksonFeature;

public class RestConsumer 
{
    public static void main(String[] args) {
        int idCliente = 23;
        
        ClientConfig cc = new ClientConfig().register(new JacksonFeature());
        Client client = ClientBuilder.newClient(cc);
        WebTarget rootTarget = client
                .target("http://cmoralesflap:8080/MiServicio/");
        WebTarget pagosTarget = rootTarget.path("pagos").path("{idCliente}")
                .resolveTemplate("idCliente", idCliente);

        Invocation.Builder builder = pagosTarget.request(MediaType.TEXT_PLAIN);
        builder.accept(MediaType.APPLICATION_JSON);
        
        List<Pago> pagoResponse = 
                builder.get().readEntity(new GenericType<List<Pago>>(){});
        
        for(Pago pago : pagoResponse) {
            System.out.println("Pago " + pago);    
        }
        
        client.close();         
    }
}

Algunas notas importantes el archivo de configuración:

  • List pagoResponse. Ahora la respuesta esperada es una lista de objetos Pago.
  • readEntity(new GenericType<List<Pago>>(){}). Indicamos que esperamos una lista de objetos.

Nota. He modificado el recurso REST para que me devuelva una colección de Pagos. A continuación adjunto los cambios requeridos en el recurso publicado.

@Path("pagos/{idCliente}")
public interface IPagoResource {
    @GET
    @Produces("application/json")
    List getPagoByIdCliente(@PathParam("idCliente") int idCliente);
}

public class PagoResource implements IPagoResource {
    public List<Pago> getPagoByIdCliente(int idCliente) {
        List<Pago> pagos = new ArrayList<Pago>();
        pagos.add(new Pago (23, new BigDecimal("520.56")));
        pagos.add(new Pago (23, new BigDecimal("380.08")));
        return pagos;
    }
}


Ahora creamos la clase Pago en el paquete mx.org.miempresa

package mx.org.miempresa;

import java.math.BigDecimal;
import org.apache.commons.lang3.builder.ToStringBuilder;

public class Pago {
    private int id;
    private int idCliente;
    private BigDecimal cantidad;

    public Pago() {
    }

    public int getIdCliente() {
        return idCliente;
    }

    public void setIdCliente(int idCliente) {
        this.idCliente = idCliente;
    }

    public BigDecimal getCantidad() {
        return cantidad;
    }

    public void setCantidad(BigDecimal cantidad) {
        this.cantidad = cantidad;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }
    
    @Override
    public String toString() {
        return new ToStringBuilder(this).append("idCliente", idCliente)
                .append("cantidad", cantidad).toString();
    }
}

Compilando la aplicación:

Para compilar la aplicación ejecutamos el siguiente comando:

gradle build

Ejecutando la aplicación:

Para ejecutar la aplicación ejecutamos el siguiente comando:

gradle execute
Ejecutando la aplicación

Bueno, hasta aquí terminamos el consumo del recurso REST. En la siguiente entrada veremos como crear filtros e interceptores.

jueves, 25 de septiembre de 2014

Consumiendo recursos REST con Jersey

Jersey provee un API para extender las herramientas proporcionadas por JAX-RS con características extras y utilidades para facilitar la creación y consumo de servicios REST.

En esta ocasión veremos el consumo de recursos REST por medio de Jersey. Jersey contiene una librería para el cliente que puede ser utilizada para probar o construir un cliente real en java. La clase Client es el punto de configuración principal para construir un cliente de servicios REST.

Vamos a consumir el recurso pagos/{idCliente} creado en la entrada anterior con la librería de Jersey:

Agregando la dependencia del cliente

Para poder utilizar Jersey en nuestro cliente, vamos a incorporar la siguiente dependencia:
<dependency>
    <groupId>org.glassfish.jersey.core</groupId>
    <artifactId>jersey-client</artifactId>
    <version>2.11</version>
    <scope>provided</scope>
</dependency>

Creando la instancia de Client

import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.WebResource;

Client client = ClientBuilder.newClient();

Ubicando el recurso

Una vez que tenemos la instancia de la clase Client crearemos un WebTarget que representa el contexto root de la aplicación REST.

WebTarget rootTarget = client.resource("http://cmoralesflap:8080/MiServicio/");

Ahora que ya tenemos el WebTarget apuntando al contexto root, crearemos otro WebTarget para el recurso que deseamos consumir. Como se puede observar la instancia de WebTarget puede ser utilizada para derivar otros WebTargets.

WebTarget pagosTarget = rootTarget.path("pagos").path("{idCliente}")
                .resolveTemplate("idCliente", idCliente);

Puesto que nuestro recurso utiliza Path Parameters en la solicitud del cliente debemos especificarla por medio de parámetros de template. Los parámetros de template son especificador envolviendo los parámetros por medio de llaves. Una vez especificados debemos llamar al método resolveTemplate para sustituir el {idCliente}, con el valor real.

Consumiendo el recurso

Ya hemos ubicado el recurso, ahora solo queda que hagamos el consumo
Invocation.Builder builder = pagosTarget.request(MediaType.TEXT_PLAIN);
        builder.accept(MediaType.APPLICATION_JSON);     
        Response respuesta = builder.get();
Como vemos el consumo del recurso es muy descriptivo:
  • Enviamos la información en texto plano. MediaType.TEXT_PLAIN "text/plain"
  • Recibimos la información en formato JSON. MediaType.APPLICATION_JSON "application/json"
Por último dejo el código completo del consumo que hemos realizado.
public static void main(String[] args) {
        int idCliente = 23;
        Client client = ClientBuilder.newClient();
        WebTarget rootTarget = client
                .target("http://cmoralesflap:8080/MiServicio/");
        WebTarget pagosTarget = rootTarget.path("pagos").path("{idCliente}")
                .resolveTemplate("idCliente", idCliente);

        Invocation.Builder builder = pagosTarget.request(MediaType.TEXT_PLAIN);
        builder.accept(MediaType.APPLICATION_JSON);
        Response respuesta = builder.get();
        System.out.println("Estatus " + respuesta.getStatus());
        System.out.println("Respuesta " + respuesta.readEntity(String.class));
        client.close();
    }

No olvidemos invocar el método cerrar en el cliente. Las instancias de Client son objetos muy pesados. Por razones de rendimiento, debemos limitar el número de instancias en la aplicación, debido a que la inicialización y destrucción de estas es muy costoso en el ambiente de ejecución.

Consumiendo recurso REST Listo. En la siguiente entrada veremos como consumir el recurso y deserializar la respuesta JSON a una estructura de objetos.

lunes, 15 de septiembre de 2014

Publicación con representación JSON y XML en JAX-RS



Hace poco tiempo hice una presentación de REST en la empresa en la que trabajo. El objetivo de la presentación era mostrar una implementación de REST por medio del API JAX-RS.

Durante la presentación hicimos un pequeño ejemplo con las tecnologías: Jax-RS + Maven + GlassFish + Jersey la cual comparto.

Creacion de proyecto web.

Partiremos de un proyecto web vacío e iremos incorporando las dependencias requeridas para la creación del servicio REST.

Vamos a colocarnos en el directorio en el que crearemos nuestro proyecto y ejecutaremos el siguiente comando de maven:
mvn archetype:generate -DgroupId=mx.org.miempresa.miservicio -DartifactId=MiServicio -DarchetypeArtifactId=maven-archetype-webapp

En version colocar 1.0-SNAPSHOT
Teclear "Y" + enter

Agregar dependencia de JAX-RS

Ahora necesitamos declarar la dependencia de JAX-RS Puesto que usaremos GlassFish no necesitamos empaquetar nada con nuestra aplicación. El servidor ya tiene todo incluido.

<dependency> 
  <groupId>javax.ws.rs</groupId> 
  <artifactId>javax.ws.rs-api</artifactId> 
  <version>2.0</version> 
  <scope>provided</scope> 
</dependency>
JAX-RS Annotations. JAX-RS utiliza anotaciones para indicar los recursos, verbos, filtros, interceptores, parámetros, ..., utilizados en REST. La siguiente lista presenta algunas de las anotaciones utilizadas dentro del API. Para consulta de la lista de anotaciones disponibles consultar la pagína de Jersey.
  • @Path. Etiqueta para identificar un recurso que nos interesa publicar.
  • @Produces. Especifica el tipo de MIME que el recurso produce y envía al cliente.
  • @Consumes. Especifica el tipo de MIME que el recurso consume y que es recibido del cliente.
  • @Provider. Utilizado para cualquier cosa de interés para JAX-RS como MessageBodyReader y MessageBodyWriter.
Request Method Designators. </ br>Para indicar los verbos HTTP a utilizar, JAX-RS proporciona las siguientes anotaciones:
  • @Get. Http GET requests.
  • @PUT. Http PUT requests.
  • @POST. Http POST requests.
  • @DELETE. Http DELETE requests.
  • @HEAD. Http HEAD requests.
Extrayendo parámetros Con JAX-RS podemos extraer los siguientes tipos de parámetros de un recurso:
  1. Query. Este parámetro se extrae mediante la anotación @QueryParam. Si queremos un valor por default cuando el parámetro no es enviado, podemos utilizar la anotación @DefaultValue.
  2. URI path. Este parámetro se extrae mediante la anotación @PathParam.
  3. Form. Extrae la información del request con representación de tipo MIME "application/x-www-form-urlencoded" por medio de la anotación @FormParam
  4. @POST
    @Consumes("application/x-www-form-urlencoded")
    public void post(@FormParam("name") String name) {
        // Store the message
    }
    
  5. Cookie. Permite extraer información de los cookies declarados por medio de la anotación @CookieParam.
  6. Header. Permite extraer información de los headers por medio de la anotación @HeaderParam.
  7. Matrix.Permite extraer información de los segmentos de la URL. Los parametros matrix son un conjunto de llave=valor. por ejemplo: /libros/2014;autor=clerks En la URL anterior, el parámetro matrix es autor=clerks separado por un ';'
Creación del recurso Como primer ejemplo haremos un recurso con el verbo GET.
Vamos a crear la siguiente clase en el paquete mx.org.miempresa.miservicio.entities:
@Path("pagos/{idCliente}")
public class PagoResource {
    @GET
    @Produces("application/json")
    public String getPagoById(@PathParam("idCliente") int idCliente) 
    {
         return "{\"cantidad\":520,\"idCliente\":23}";
    }
}

Registro del recurso
Para que nuestro recurso sea visible, será necesario indicar su existencia por medio de la implementación de la clase Application. Vamos a crear una clase llamada MiServicioApplication en el paquete mx.org.miempresa.miservicio.
@ApplicationPath("/")
public class MiServicioApplication extends Application {
    @Override
    public Set<Class<?>> getClasses() {
        Set<Class<?>> el = new HashSet<Class<?>>();
        el.add(PagoResource.class);
        return el;
    }
}

Compilación y empaquetamiento del recurso
Ahora necesitamos el WAR para desplegarlo en nuestro servidor. Para generar el war iremos al directorio de nuestra aplicación y ejecutaremos el siguiente comando de maven:
mvn clean package

Despliegue del servicio
Es hora de desplegar nuestro recurso. Para poder desplegar nuestro recurso realizamos los siguentes pasos:
  1. Iniciar el servidor de glassfish.
  2. Abrir un navegador y teclear la siguiente dirección: localhost:puerto
  3. Click en aplicaciones
  4. Click en desplegar
  5. Click en seleccionar archivo
  6. Seleccionar el war recien creado
  7. Click en ok

Prueba de consumo del recurso
CURL es una herramienta de línea de comandos para transferir datos con sintaxis URL. Para probar el consumo de nuestro recurso instalaremos esta herramienta.
Una vez instalada ejecutamos el siguiente comando:
curl http://host:port/context/pagos/2

Donde context corresponde al nomnbre asignado al campo "context root" solicitado por glassFish al desplegar el servicio. Por ejemplo yo invoco el siguiente comando: http://cmoralesflap:8080/coco/pagos/2

Por último dejo los comandos para los demás verbos de HTTP con CURL:
GET
curl http://host:port/context/pagos/2
POST
curl –X POST -d "data" URI
curl -H "Accept: application/json" -H "Content-Type: application/json" -d '{\"cantidad\":520,\"idCliente\":23}' http://host:port/context/pagos/2
PUT
curl -X PUT -d "data" URI
DELETE
curl -X DELETE URI

Algunos parámetros útiles:
    -i. Shows response header
    -H. Pass request headers to the resource
    -H "Accept: application/json“
    -H "Content-Type: application/json"
    -X. Pass a HTTP method name
    d. Pass parameters enclosed in quotes. Multiple parameters are separated by ‘&’

En la siguiente entrada publicaremos un recurso mediante POST y haremos uso de la serialización con JSON y XML para la negociación de la representación.

Publicación con representación JSON y XML en JAX-RS

En la publicación anterior creamos y publicamos un recurso mediante el verbo GET con JAX-RS. En esta entrada continuaremos con el ejercicio mediante la publicación del recurso con el verbo POST y expondremos el recurso con el formato JSON y XML.

Separación de la implementación del recurso y su interfaz


Una de las ventajas del uso de JAX-RS es que podemos utilizar una interfaz para exponer nuestro recurso y de esta forma eliminar las anotaciones de la clase que implementa. Esto hace más legible el código de la implementación ademas de permitirnos encapsular en la interfaz los detalles de la tecnología que utilizaremos para exponer nuestro recurso; de esta forma podríamos cambiar las anotaciones de la interfaz y así exponer nuestro recurso mediante SOAP con JAX-WS.

Vamos a crear la siguiente interfaz:
@Path("pagos/{idCiente}")
public interface IPagoResource {
    @Produces("application/json")
    @GET
    String getPagoByIdCliente(@PathParam("idCliente") int idCliente);
}

Ahora crearemos la clase que implementará la interfaz y representará nuestro recurso:
public class PagoResource implements IPagoResource 
{
    public String getPagoByIdCliente(int idCliente) {        
        return "\"pagos\":[{\"cantidad\":520,\"idCliente\":23}, " +
        "{\"cantidad\":520,\"idCliente\":23}]";
    }
}


Despliegue del recurso:

Para desplegar el recurso y verificar que podamos invocar el recurso de pagos mediante el verbo GET, con contenido JSON ejecutamos los siguientes pasos:
  1. Abrir una terminal
  2. Navegar al directorio de nuestra aplicación
  3. Ejecutar el comando maven:
  4. mvn clean package
    
  5. Abrir glassFish y redesplegar el war
  6. Ejecutar el siguiente comando:
  7. curl -X GET -H "Accept:application/json" http://cmoralesflap:8080/MiServicio/pagos/2
    

    Nota. MiServicio debe ser remplazado por el contexto elegido al desplegar nuestra aplicación.

POST CON JSON


Una vez hecha la separación del recurso y su interfaz, ahora vamos a exponer al recurso para la recepción de pagos mediante el verbo POST y tipo de contenido JSON.

Primero necesitamos crear la clase Pago en el paquete mx.org.miempresa.miservicio.to
public class Pago 
{
    private int id;
    private BigDecimal cantidad;
    private int idCliente;
}
Nota. Se omiten los accessors y mutators para simplificar el código.

Vamos a crear ahora la operación en la interfaz IPagoResource para exponer nuestro recurso
@Path("pagos/{idCliente}")
public interface IPagoResource {
    @POST
    @Produces("application/json")
    @Consumes("application/json")    
    Pago savePago(Pago pago);
}

Algunos puntos a resaltar de la operación expuesta son los siguientes:
  • JAX-RS por default da soporte para exponer tipo de recursos JSON mediante la librería Jackson.
  • La anotación @POST Indica que este recurso será invocado utilizando el verbo POST.
  • @Produces("application/json") Indica que el recurso produce como salida tipo de contenido JSON, mismo que el cliente deberá procesar.
  • @Consumes("application/json") Indica que el cliente deberá consumir el recurso mediante el envío de contenido JSON, el cual representa el pago realizado por un cliente.

Vamos a implementar la funcionalidad de nuestro recurso expuesto:
public class PagoResource implements IPagoResource {
    public Pago savePago(Pago pago) 
 {
        pago.setId(23);
        return pago;
    }
}

Como podemos observar la operación retorna la entrada que ha recibido, agregando el identificador del pago generado.

Para desplegar el recurso y verificar que podamos invocar el recurso de pagos mediante el verbo POST, con contenido JSON ejecutamos los pasos antes definidos en la sección de Despliegue del recurso e intercambiamos el comando del paso 5 por el siguiente:

curl -X POST -H "Accept: application/json" -H "Content-Type: application/json" -d '{\"cantidad\":520,\"idCliente\":23}' http://host:puerto/MiServicio/pagos/2
Comando POST con curl

POST CON XML

JAXB. Permite a los desarrolladores mapear clases Java a representaciones XML. JAXB provee dos características principales: Permite serializar y deserializar clases java a XML y viceversa; además de permitir la generación de un esquema para la validación de la representación.

Para poder exponer nuestro recurso con representación XML seguimos los siguientes pasos:

  1. Agregar la siguiente dependencia al POM del proyecto:
  2. <dependency>
        <groupId>javax.xml.bind</groupId>
        <artifactId>jaxb-api</artifactId>
        <version>2.2.6</version>
    </dependency> 
    
  3. Modificar el recurso para producir JSON y XML
  4.     @POST
        @Produces({"application/json", "application/xml"})
        @Consumes("application/json")    
        Pago savePago(Pago pago);
    
  5. Agregar la anotación @XmlRootElement a la entidad de Pago. Esta anotación indica cual será el root del xml a generar. En este caso pago puesto que no le definimos un nombre en los atributos de la anotación.
  6. Redesplegar el recurso
  7. Modificar el comando curl para que acepte respuesta en formato XML
  8. curl -X POST -H "Accept: application/xml" -H "Content-Type: application/json" -d '{\"cantidad\":520,\"idCliente\":23}' http://host:puerto/MiServicio/pagos/2
    

La siguiente imagen muestra el comando curl con el verbo POST para negociar la representación del recurso de pago.

Comando curl con verbo POST y negociación de representación

Como podemos ver desplegar el recurso con formato JSON y XML ha sido relativamente sencillo y flexible para la negociación del formato requerido por el cliente. En la siguiente entrada veremos como publicar nuestro recurso con los verbos POST y DELETE.